2013-01-24 9 views
7

Odnosi się to do aplikacji PHP 5.3 Cli, która przetwarza wiele danych w złożony sposób, trwające wiele godzin. Ktoś odkrył, że wyłączenie zbierania śmieci sprawiło, że działało znacznie szybciej (może aż 50%).Dlaczego moduł do zbierania śmieci zwolnił wydajność i jak zarządzać pamięcią bez tego?

Jedyny artykuł, na który natknąłem się na ten wynik, to http://derickrethans.nl/collecting-garbage-performance-considerations.html. Nie jestem pewien, czy podążam za nim w całości, ale wydaje się sugerować, że dotyczy to tylko kodu z dużą liczbą odwołań cyklicznych.

Czy ktoś może rzucić trochę światła na to?

Ponadto, skoro wyłączyliśmy gc, czy istnieje sposób ręcznego zmniejszenia pamięci? Używanie unset() zostało zasugerowane. Szybki test wykazał, że osiemdziesiąt bajtów jest zwalnianych przez unset() niezależnie od wielkości obiektu. Sugeruje to, że jest to po prostu zaburzenie odniesienia, co jest potwierdzone przez to, co przeczytałem online. Czy mam rację, sądząc, że te osiemdziesiąt bajtów i tak zostanie uwolnione, nawet bez zbędnego zbierania śmieci, kiedy zmienna wypadnie poza zasięgiem?

Odpowiedz

4

Po prostu wyłączyłeś okrągłe odniesienie-GC. Ten regularny nadal działa.

Typowe testy GC, niezależnie od tego, czy istnieją zval s ("pamięć"), które nie są przywoływane żadnymi zmiennymi ani właściwościami i zwolnią tę pamięć. Cykliczne odniesienia jest, gdy dwa lub więcej obiektów odniesienia sobą bezpośrednio lub pośrednio

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

Teraz oba obiekty odwoływać się nawzajem, ale nie są one zarówno odwołuje się od nigdzie indziej, więc są nieosiągalne. To jest to, co GC próbuje wykryć, ale aby je znaleźć, iteruje nad każdym znanym obiektem i dowiaduje się, czy istnieje odniesienie "z zewnątrz". To trochę bardziej skomplikowane, ale uproszczone to;) Więc w konstrukcjach z wieloma referencjami, szczególnie kołowymi, jest to ogromne zadanie.

Warto wspomnieć: Z numerem unset() usuwa się tylko referencję, ale nie wolno jej zwalniać (bezpośrednio). Robi to później GC (i robi dobrą robotę :))

+0

Myślałem, że to może być przypadek, ale instrukcja jest bardzo niejasna. Więc, jest to normalne niemożliwe do wyłączenia? (Nie, że chcę, po prostu zainteresowany) – naomi

+0

@naomi Tak, nie można go wyłączyć. Nie ma też powodu (wpływ jest bardzo mały) i dlatego, że nie możesz zwolnić pamięci z poziomu samego PHP ('unset()' nie robi tego) będziesz miał ogromny problem: D – KingCrunch

2

Możesz ręcznie usunąć cykle z kodu przy użyciu unset. Oczyszczasz cykle z klas, implementując funkcję __destruct. unset wszystkie prywatne, chronione lub publiczne zmienne, które odwołują się do innych obiektów.

To bardzo uciążliwe, jeśli aplikujesz do istniejącego programu, ale jest to wykonalne.


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

to robi nie praca:

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

To działa:

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

Dostajesz moje ' + 1' dla drugiego użycia '__destruct()' Znajduję użyteczną _ever_: D (Pierwsza to "zamykanie zasobów, połączenia DB, pliki, ...") – KingCrunch

+0

OK, więc unset() nie zwalnia pamięci, ale usuwa odwołania kołowe, więc dobrym pomysłem jest jej użycie (w razie potrzeby), gdy okrągły kolektor odniesienia jest wyłączony. Czy mam to prawo? – naomi

+0

Tak, możesz użyć 'unset' do usunięcia cykli.' Unset' samo nie zwalnia pamięci, czyści odniesienia, a jeśli fragment danych nie ma referencji, to jest czyszczone. GC łatwo zlicza referencje , ale trudne do wykrycia cykli – Halcyon

Powiązane problemy