2011-10-25 16 views
6

Un mio collega ha scritto una sceneggiatura che stava esaurendo la memoria disponibile. Ho ristretto la scelta a caso seguente prova di base:utilizzo memoria di tipo exessive quando non si deseleziona esplicitamente

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

Questo rompe sulla terza iterazione:

3116696 
49123440 

49123440 
95518368 

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

sono riuscito a risolvere il problema, semplicemente aggiungendo un unset() chiamata nel circuito:

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'); 

lo script passa attraverso la sua 20 iterazioni piacevole e scorrevole, con un utilizzo della memoria relativamente costante:

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 

Questo mi confonde! Il garbage collector non dovrebbe ripulire l'oggetto, poiché la variabile è stata sovrascritta? Sto eseguendo PHP 5.3, quindi i riferimenti circolari non possono essere la causa di questo problema.

+0

@Rjik: sei sicuro che ci siano riferimenti a questi oggetti da qualche altra parte? nessun caching, nessun modello di identità? – RageZ

+0

potrebbe essere sciocco ... ma cosa succede se si disattiva prima di creare un nuovo '$ Sondaggio'? – k102

+0

@RageZ: sì, penso di esserne sicuro. Ci sono comunque riferimenti nell'oggetto. – Rijk

risposta

4

Circular references can still be a problem in 5.3:

pulitura Problemi

Anche se non c'è un simbolo in qualsiasi applicazione che punta a questo struttura, non può essere pulito perché l'elemento di matrice "1" ancora punti a questo stesso array. Poiché non vi è alcun simbolo esterno puntato ad esso, non c'è modo per un utente di pulire questa struttura; si ottiene così una perdita di memoria. Fortunatamente, PHP pulirà questa struttura di dati alla fine della richiesta, ma prima di allora, questo sta prendendo spazio prezioso in memoria. Questa situazione si verifica spesso se sei che implementa algoritmi di analisi o altre cose in cui hai un figlio punto indietro in un elemento "padre". La stessa situazione può anche verificarsi con gli oggetti con oggetti, dove è più probabile che si verifichi, poiché gli oggetti vengono sempre utilizzati implicitamente per riferimento.

Probabilmente c'è anche qualche risorsa di memoria-hogging all'interno di Survey che occupa tutta questa memoria; il comportamento osservato dovrebbe essere una combinazione di un ciclo di ref e una tale risorsa.

Cosa c'è esattamente nello Survey?

+1

+1 per "memoria-hogging", stiamo parlando di 40-45 MiB per oggetto qui. E 'davvero necessario? – CodeCaster

+0

'Survey' rappresenta un questionario _ veramente grande, con oltre 4000 domande (rappresentate da oggetti secondari contenuti nell'oggetto' Survey', che a loro volta contengono un oggetto per la risposta). Leggendo il link, credo che il mio problema sia probabilmente causato da riferimenti circolari; gli oggetti 'Domanda' hanno un riferimento al 'Sondaggio' in essi. In ogni caso, vedrò di ridurre l'utilizzo della memoria. Qualsiasi altra soluzione/best practice per questo (oltre all'uso di 'unset()' sempre)? – Rijk

+1

@Rijk: Solo uno: non progettare le classi in modo che un'istanza possa occupare così tanta memoria (utilizzare invece il caricamento dei dati pigro o su richiesta esplicita). Questo non ti aiuterà qui, ma "unset ($ survey)" è una soluzione davvero economica per questo problema. – Jon

0

Il problema è stato causato dalla combinazione di riferimenti circolari e gli oggetti che occupano 1/3 della memoria disponibile ciascuno. Modifica del codice a questo:

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'); 

mi ha mostrato:

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 

Si può vedere chiaramente che cottura fino il garbage collector libera manualmente la memoria occupata dall'oggetto 'orfano'. Penso che il problema fosse che non c'era abbastanza tempo per il bidone della spazzatura, perché gli oggetti erano così grandi.

La soluzione più semplice per ora è l'aggiunta della chiamata unset() - a lungo termine, tuttavia, studierò i modi per rendere l'oggetto Survey più efficiente in termini di memoria.

Grazie a Jon e CodeCaster per avermi indicato nella giusta direzione!

Problemi correlati