php - para - manual de programacion android pdf
Forzar liberación de memoria en PHP (8)
Descubrí que es más probable que se invoque el administrador de memoria interna de PHP al completar una función. Sabiendo eso, he refactorizado el código en un bucle así:
while (condition) {
// do
// cool
// stuff
}
a
while (condition) {
do_cool_stuff();
}
function do_cool_stuff() {
// do
// cool
// stuff
}
EDITAR
Ejecuté este rápido punto de referencia y no vi un aumento en el uso de la memoria. Esto me lleva a creer que la fuga no está en json_decode()
for($x=0;$x<10000000;$x++)
{
do_something_cool();
}
function do_something_cool() {
$json = ''{"a":1,"b":2,"c":3,"d":4,"e":5}'';
$result = json_decode($json);
echo memory_get_peak_usage() . PHP_EOL;
}
En un programa PHP, leo secuencialmente un montón de archivos (con file_get_contents
), gzdecode
, json_decode
el resultado, json_decode
el json_decode
, json_decode
la json_decode
y json_decode
alrededor del 1% en una matriz.
Desafortunadamente, con cada iteración (atravieso una matriz que contiene los nombres de archivo), parece que se pierde algo de memoria (de acuerdo con memory_get_peak_usage
, alrededor de 2-10 MB cada vez). He revisado doble y triple mi código; No estoy almacenando datos innecesarios en el bucle (y los datos necesarios apenas superan los 10 MB en general), pero a menudo reescribo (en realidad, cadenas en una matriz). Aparentemente, PHP no libera la memoria correctamente, por lo que utiliza más y más RAM hasta que llega al límite.
¿Hay alguna manera de hacer una recolección de basura forzada? ¿O, al menos, averiguar dónde se utiliza la memoria?
En PHP> = 5.3.0, puede llamar a gc_collect_cycles()
para forzar un pase GC.
Nota: zend.enable_gc
tener zend.enable_gc
habilitado en su php.ini
habilitado, o llamar a gc_enable()
para activar el recopilador de referencia circular.
Encontré la solución: era una concatenación de cuerdas. Estaba generando la entrada línea por línea al concatenar algunas variables (la salida es un archivo CSV). Sin embargo, parece que PHP no libera la memoria utilizada para la copia antigua de la cadena, por lo tanto, de manera efectiva obstruye la RAM con los datos no utilizados. Cambiar a un enfoque basado en matrices (e implosionarlo con comas justo antes de colocarlo en el archivo de salida) evitó este comportamiento.
Por alguna razón, no es obvio para mí, PHP informó el aumento en el uso de memoria durante las llamadas a json_decode, lo que me lleva a suponer erróneamente que la función json_decode era el problema.
Iba a decir que no necesariamente esperaría que gc_collect_cycles () resolviera el problema, ya que, presumiblemente, los archivos ya no están asignados a zvars. Pero, ¿comprobaste que gc_enable fue llamado antes de cargar algún archivo?
Me he dado cuenta de que PHP parece engullir la memoria cuando se incluye incluye, mucho más de lo que se requiere para la fuente y el archivo con token, esto puede ser un problema similar. Sin embargo, no estoy diciendo que esto sea un error.
Creo que una solución alternativa sería no usar file_get_contents sino fopen () .... fgets () ... fclose () en lugar de mapear todo el archivo en la memoria de una sola vez. Pero tendrías que intentarlo para confirmar.
HTH
DO.
Llame a memory_get_peak_usage()
después de cada declaración, y asegúrese de que unset()
todo lo que pueda. Si está iterando con foreach()
, use una variable de referencia para evitar hacer una copia del original ( foreach() ).
foreach( $x as &$y)
Si PHP está perdiendo memoria, una recolección de basura forzada no hará ninguna diferencia.
Hay un buen artículo sobre las fugas de memoria de PHP y su detección en IBM
Recientemente hubo un problema similar con System_Daemon . Hoy he aislado mi problema a file_get_contents
.
¿Podrías intentar usar fread
en fread
lugar? Creo que esto puede resolver tu problema. Si lo hace, es probable que sea hora de hacer un informe de error en PHP.
Tiene que ver con la fragmentación de la memoria.
Considere dos cadenas, concatenadas a una cadena. Cada original debe permanecer hasta que se cree la salida. La salida es más larga que cualquiera de las entradas.
Por lo tanto, se debe realizar una nueva asignación para almacenar el resultado de dicha concatenación. Las cadenas originales están liberadas pero son pequeños bloques de memoria.
En un caso de ''str1'' . ''str2'' . ''str3'' . ''str4''
''str1'' . ''str2'' . ''str3'' . ''str4''
''str1'' . ''str2'' . ''str3'' . ''str4''
tienes varios temps siendo creados en cada uno. - y ninguno de ellos cabe en el espacio que se ha liberado. Es probable que las cadenas no estén dispuestas en la memoria contigua (es decir, cada cadena es, pero las distintas cadenas no se colocan de extremo a extremo) debido a otros usos de la memoria. Por lo tanto, liberar la cadena crea un problema porque el espacio no se puede reutilizar de manera efectiva. Así que creces con cada tmp que creas. Y no reutilizas nada, nunca.
Usando el implode basado en matriz, creará solo 1 salida, exactamente la longitud que necesita. Realizando solo 1 asignación adicional. Así que es mucho más eficiente en memoria y no sufre la fragmentación de la concatenación. Lo mismo es cierto de python. Si necesita concatenar cadenas, más de 1 concatenación siempre debe estar basada en matrices:
''''.join([''str1'',''str2'',''str3''])
en pitón
implode('''', array(''str1'', ''str2'', ''str3''))
en PHP
Los equivalentes de sprintf también están bien.
La memoria reportada por memory_get_peak_usage es básicamente siempre el "último" bit de memoria en el mapa virtual que tenía que usar. Así que ya que siempre está creciendo, reporta un rápido crecimiento. A medida que cada asignación cae "al final" del bloque de memoria utilizado actualmente.
Acabo de tener el mismo problema y encontré una posible solución.
SITUACIÓN: Estaba escribiendo desde una consulta de db en archivos csv. Siempre asigné una fila de $, luego la reasigné en el siguiente paso. Unsetting $ row no ayudó; poner una cadena de 5MB en la fila $ primero (para evitar la fragmentación) no ayudó; crear una matriz de $ row-s (cargar muchas filas en ella + desarmar todo en cada paso 5000) no ayudó; Realmente probé un par de cosas.
PERO.
Cuando hice una función separada que abre el archivo, transfiere 100,000 líneas (lo suficiente para no consumir toda la memoria) y cierra el archivo, ENTONCES hice llamadas subsiguientes a esta función (adjuntando al archivo existente), encontré que para Cada función de salida, PHP eliminó la basura. Era una cosa local de espacio variable.
CONCLUSIÓN: Cada vez que su función sale, libera todas las variables locales.
Esta es la regla, por lo que he descubierto. Sin embargo, solo una nota al margen: cuando intenté que mi función "do_only_a_smaller_subset ()" obtuviera algunas variables por referencia (es decir, el objeto de consulta y el puntero del archivo), la recolección de basura no se realizó. Ahora tal vez no estoy entendiendo algo y quizás el objeto de consulta (mysqli) tenía una fuga, bueno, no lo sé. Sin embargo, dado que fue aprobado por la referencia, obviamente no se pudo limpiar ya que existía después del punto de salida de la pequeña función.
Por lo tanto, vale la pena intentarlo! Me salvó el día descubrir esto.