2013-01-24 15 views
7

Si riferisce a un'applicazione Cli di PHP 5.3 che elabora molti dati in modo complesso, impiegando ore per essere eseguito. Qualcuno ha scoperto che la disattivazione della garbage collection lo ha reso molto più veloce (forse fino al 50%).Perché il garbage collector di PHP rallenta la perfomance e come gestire la memoria senza di essa?

L'unico articolo che ho trovato che menziona questo risultato è http://derickrethans.nl/collecting-garbage-performance-considerations.html. Non sono sicuro di seguirlo del tutto, ma sembra suggerire che si applica solo al codice con molti riferimenti circolari.

Qualcuno potrebbe far luce su questo per favore?

Inoltre, dato che abbiamo disattivato gc, esiste un modo per ridurre manualmente la memoria? È stato suggerito l'uso di unset(). Un rapido test ha dimostrato che ottanta o più byte sono liberati da unset() indipendentemente dalla dimensione dell'oggetto. Questo suggerisce che si sta solo mettendo in pausa il riferimento, che è confermato da ciò che ho letto online. Ho ragione nel pensare che questi ottanta byte sarebbero comunque liberati, anche senza la garbage collection, quando la variabile non rientra nello scope?

risposta

4

Hai appena disattivato il GC di riferimento circolare. Quello normale funziona ancora.

I test comuni del GC, indipendentemente dal fatto che ci siano o meno zval s ("memoria"), che non sono più referenziati da nessuna variabile o proprietà e libereranno questa memoria. Un riferimento circolare è, quando due o più oggetti riferimenti tra loro direttamente, o indirettamente

$a = new stdClass; 
$b = new stdClass; 
$a->b = $b; 
$b->a = $a; 
unset($a, $b); 

Ora entrambi gli oggetti di riferimento l'un l'altro, ma entrambi sono non richiamati da qualsiasi altro luogo, in modo che siano irraggiungibili. Questo è ciò che il GC di riferimento circolare prova a rilevare, ma per trovarli, itera su ogni oggetto conosciuto e lo rileva, se esiste un riferimento "dall'esterno". È un po 'più complicato, ma è semplicissimo;) Quindi in strutture con molti riferimenti, specialmente quelli circolari, è un compito enorme.

Vale la pena menzionare: con unset() si rimuove solo il riferimento, ma non si libera la memoria (direttamente). Questo è fatto dal GC più tardi (e fa un buon lavoro :))

+0

ho pensato che potrebbe essere il caso, ma il manuale è molto chiaro. Quindi, il normale è impossibile da disabilitare? (Non che io voglia, solo interessato) – naomi

+0

@naomi Sì, non puoi disabilitarlo. Non c'è anche alcun motivo (l'impatto è molto piccolo) e poiché non è possibile liberare la memoria da PHP personalmente ('unset()' non lo fa) si avrà un grosso problema: D – KingCrunch

2

È possibile rimuovere manualmente i cicli dal proprio codice utilizzando unset. Si puliscono i cicli dalle classi implementando una funzione __destruct. unset tutte le variabili private, protette o pubbliche che fanno riferimento ad altri oggetti.

Diventa davvero noioso se si sta applicando questo a un programma esistente ma è fattibile.


class A { 
    public $ref; 
    public function __destruct() { 
     unset($this->ref); 
     echo "destruct"; 
    } 
} 
$a1 = new A(); 
$a2 = new A(); 
$a1->ref = $a2; 
$a2->ref = $a1; 

Questo fa non lavoro:

unset($a1, $a2); 
echo "--"; 
// prints --destructdestruct (in 5.3) 

Questo funziona:

$a1->__destruct(); 
unset($a1, $a2); 
echo "--"; 
// prints destructdestructdestruct-- (in 5.3) 
+0

Ottiene il mio ' + 1' per il secondo utilizzo di '__destruct()' Trovo utile _ever_: D (Il primo era "chiusura di risorse, DB-conncections, file, ...") – KingCrunch

+0

OK, quindi, unset() non libera memoria, ma rimuove i riferimenti circolari, quindi è una buona idea utilizzarlo (dove appropriato) quando il raccoglitore circolare di riferimento è disabilitato. – naomi

+0

Sì, puoi usare 'unset' per rimuovere i cicli.' Unset' stesso non libera memoria, pulisce i riferimenti e se un dato non ha riferimenti, è pulito. È facile per il GC contare i riferimenti – Halcyon