2011-10-25 10 views
6

Un colega mío escribió un script que estaba agotando la memoria disponible. I reducido a la siguiente caso de prueba básica:uso de la memoria del objeto existente cuando no se deshace explícitamente

for ($i = 0; $i <= 20; $i ++) { 
    echo memory_get_usage(). '<br />'; 
    $Survey = new Survey(14); 
    echo memory_get_usage(). '<br /><br />'; 
} 
exit('done'); 

Esto rompe en la tercera iteración:

3116696 
49123440 

49123440 
95518368 

95518368 
[E_ERROR] Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes) 

me las arreglé para solucionar este problema, simplemente añadiendo una llamada unset() en el bucle:

for ($i = 0; $i <= 20; $i ++) { 
    echo memory_get_usage(). '<br />'; 
    $Survey = new Survey(14); 
    unset($Survey); 
    echo memory_get_usage(). '<br /><br />'; 
} 
exit('done'); 

Ahora el guión pasa a través de sus 20 iteraciones agradable y suave, con un uso de memoria relativamente constante:

3116816 
49123488 

49123488 
50691656 

50691656 
51088912 

51088912 
51079064 

51079064 
50535368 

50535368 
50809296 

50809296 
51033392 

51033392 
51157208 

51157208 
50543856 

50543856 
50892760 

50892760 
51045160 

51045160 
51132688 

51132688 
50535968 

50535968 
50968632 

50968632 
51058080 

51058080 
51143304 

51143304 
50562136 

50562136 
51067432 

51067432 
51067768 

51067768 
51170824 

51170824 
50551712 

done 

Esto me confunde! ¿No se supone que el recolector de basura limpia el objeto, ya que su variable ha sido sobrescrita? Estoy ejecutando PHP 5.3, por lo que las referencias circulares no pueden ser la causa de este problema.

+0

@Rjik: ¿estás seguro de que hay alguna referencia a esos objetos en otro lugar? sin almacenamiento en caché, sin patrón de identidad? – RageZ

+0

puede ser una tontería ... pero, ¿qué pasa si 'desconectaste' antes de crear una nueva '$ Survey'? – k102

+0

@RageZ: sí, creo que estoy seguro. Sin embargo, hay referencias en el objeto ... – Rijk

Respuesta

4

Circular references can still be a problem en 5,3:

Cleanup Problemas

Aunque ya no es un símbolo en cualquier ámbito que apunta a esta estructura , no se puede limpiar debido a que el elemento de matriz "1" Todavía apunta a esta misma matriz. Debido a que no hay un símbolo externo señalando, no hay forma de que el usuario limpie esta estructura; así obtienes una pérdida de memoria. Afortunadamente, PHP limpiará esta estructura de datos al final de la solicitud, pero antes de eso, está tomando espacio valioso en la memoria. Esta situación ocurre a menudo si está implementando algoritmos de análisis u otras cosas donde tiene un punto secundario en un elemento "primario". La misma situación también puede ocurrir con objetos, por supuesto, donde es más probable que ocurra, ya que los objetos siempre se usan implícitamente por referencia.

Probablemente también hay algún recurso de memoria dentro de Survey que ocupa toda esta memoria; el comportamiento observado debe ser una combinación de un ciclo de ref y tal recurso.

¿Qué hay en Survey exactamente?

+1

+1 para "memorizar", estamos hablando de 40-45 MiB por objeto aquí. ¿Es eso realmente necesario? – CodeCaster

+0

'Survey' representa un _realmente_ gran cuestionario, con más de 4000 preguntas (representadas por objetos secundarios contenidos en el objeto' Survey', que a su vez contienen un objeto para la respuesta). Al leer el enlace, creo que mi problema probablemente sea causado por referencias circulares; los objetos 'Question' tienen una referencia de regreso a' Survey' en ellos. En cualquier caso, voy a echar un vistazo al recorte del uso de la memoria. ¿Alguna otra solución/mejores prácticas para esto (aparte de usar 'unset()' todo el tiempo)? – Rijk

+1

@Rijk: Solo uno: no modifique sus clases de modo que una instancia pueda ocupar tanta memoria (en su lugar, use carga de datos en espera o no explícita). Eso no te ayudará aquí, pero 'unset ($ survey)' es una gran solución rentable para este problema de todos modos. – Jon

0

El problema fue causado por la combinación de referencias circulares y los objetos ocupando 1/3 de la memoria disponible cada uno. Cambiar el código para esto:

for ($i = 0; $i <= 20; $i ++) { 
    echo memory_get_usage(). '<br />'; 
    gc_collect_cycles(); 
    echo memory_get_usage(). '<br />'; 
    $Survey = new Survey(14); 
    echo memory_get_usage(). '<br /><br />'; 
} 
exit('done'); 

me mostró:

3116456 
3116680 
49123288 

49123288 
49123288 
95518160 

95518160 
50452344 
96236360 

96236360 
50365776 
96261312 

96261312 
50477296 
96348608 

96348608 
50453072 
96349752 

96349752 
50478440 
96364872 

96364872 
50468192 
96365240 

96365240 
50478808 
96370760 

96370760 
50473712 
96366072 

96366072 
50474120 
96371448 

96371448 
50479088 
96375352 

96375352 
50478024 
96376672 

96376672 
50480408 
96374984 

96374984 
50476336 
96373032 

96373032 
50478456 
96372216 

96372216 
50475520 
96371288 

96371288 
50477528 
96378824 

96378824 
50483056 
96383992 

96383992 
50482696 
96376592 

96376592 
50475656 
96378072 

done 

Se puede ver claramente que disparar hasta el colector de basura libera manualmente la memoria ocupada por el objeto 'huérfanos'. Creo que el problema fue que simplemente no había tiempo suficiente para que el recolector de basura lo pateara, ya que los objetos eran tan grandes.

La solución más fácil por ahora es agregar la llamada unset() - a largo plazo, aunque voy a investigar maneras de hacer que el objeto Survey sea más eficiente en cuanto a la memoria.

¡Gracias a Jon y CodeCaster por señalarme en la dirección correcta!

Cuestiones relacionadas