Garbage collection štala

1 minute read

Garbage collection v PHP 5 deluje sicer zelo v redu, pojavi pa se problem s štetjem referenc na objekt v določenem primeru. Teoretično naj bi PHP sprostil pomnilnik, ko na določen objekt ne kaže več nobena referenca, vendar temu ni tako v vseh primerih. Problem nastane, ko dva objekta kažeta eden na drugega, ponavadi se take zadeve zgodijo v parent / child odnosu.

Poglejmo si primer:

class parentObject {

    protected $childObject;

    function __construct() {
        $this->childObject = new childObject($this);
    }
}

class childObject {

    protected $parentObject;

    function __construct($parentObject) {
        $this->parentObject = $parentObject;
    }
}

$objekt = new parentObject();

Zadeva izgleda cisto legalna, ko ustvarimo nov objekt razreda parentObject, ta v svojem konstruktu ustvari otroka, objekt razreda childObject, da pa se otrok zaveda čigav je, mu starš kot parameter poda referenco nase, ki si jo otrok seveda zapomni. Vse lepo in prav, do tukaj. Kaj pa se zgodi, ko objekta ne potrebujemo več? Kaj ponavadi naredimo v takem primeru? Enostavno:

unset($objekt);

Gornja koda povzroči, da se izbriše referenca na objekt, garbage collector pa, glede na to, da referenc na objekt ni več, poskrbi za to, da se ustrezno sprosti tudi pomnilnik. No, v tem primeru ne. S klicem funkcije unset() smo sicer prekinili referenco $objekt, vendar referenca $this->parentObject v otrokovem objektu še kar obstaja. Zlomka, garbage collector ne naredi nič. Če te zadeve uporabljamo v kakšni dolgi zanki, seveda lahko sklepamo kaj se zgodi. Izvajanje skripte se prekine, ko se napolni ves dovoljeni pomnilnik.

Kako to rešiti? Našel sem samo eno rešitev, ki tudi ni tako zelo slaba. Objektoma dodamo metodo destroy():

class parentObject {

    protected $childObject;

    function __construct() {
        $this->childObject = new childObject($this);
    }

    function destroy() {
        $this->childObject->destroy();
        unset($this->childObject);
    }
}

class childObject {

    protected $parentObject;

    function __construct($parentObject) {
        $this->parentObject = $parentObject;
    }

    function destroy() {
        unset($this->parentObject)
    }
}

$objekt = new parentObject();

In še, ko objekta ne potrebujemo več:

$objekt->destroy();
unset($objekt);

Kaj smo s tem naredili? Poklicali smo metodo destroy() starševega objekta, ta pokliče metodo destroy() otrokovega objekta, ki prekine referenco na svojega starša (kot je Ivan mamo zatajil), takoj zatem še starš prekine referenco na otroka (prav mu je Ivanu, jaz sem se pa tako trudila s kavo), no potem pa mi prekinemo še referenco na starša in vsemogočni garbage collector poskrbi, da se oba objekta razblineta v nič.

Problem rešen!

Updated: