Salah satu manfaat terbesar menggunakan bahasa skrip adalah Anda dapat memanfaatkan mekanisme pengumpulan sampah otomatis (membebaskan memori). Anda tidak perlu melakukan pemrosesan apa pun untuk melepaskan memori setelah menggunakan variabel, PHP akan melakukannya untuk Anda.
Tentu saja, kita dapat memanggil fungsi unset() untuk mengosongkan memori jika kita menginginkannya, namun biasanya hal tersebut tidak perlu dilakukan.
Namun, dalam PHP, setidaknya ada satu situasi di mana memori tidak akan dilepaskan secara otomatis, meskipun unset() dipanggil secara manual. Detailnya dapat ditemukan di: http://bugs.php.net/bug.php?id=33595 .
Gejala masalah: Jika ada hubungan referensi timbal balik antara dua objek, seperti "objek induk-objek anak", pemanggilan unset() pada objek induk tidak akan melepaskan memori yang mereferensikan objek induk di objek anak (bahkan jika objek induk objeknya adalah sampah yang dikumpulkan, juga tidak berfungsi).
Sedikit bingung? Mari kita lihat kode berikut:
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}}class Bar {function __construct($foo = null){$this-> foo = $foo;}} while (true) {$foo = new Foo();unset($foo);echo number_format(memory_get_usage()) . "n";}?>Jalankan kode ini dan Anda akan melihat Penggunaan memori semakin tinggi dan semakin tinggi hingga habis.
...33,551,61633,551,97633,552,33633,552,696PHP Kesalahan fatal: Ukuran memori yang diizinkan sebesar 33554432 byte habis (mencoba mengalokasikan 16 byte) di memleak.php on line 17 Untuk sebagian besar pemrogram PHP, inilah situasi ini tidak menjadi masalah.
Namun jika Anda menggunakan banyak objek yang saling mereferensikan dalam kode yang berjalan lama, terutama jika objeknya relatif besar, memori akan cepat habis.
Solusi Userland agak membosankan dan tidak elegan, tetapi tautan bug.php.net yang disebutkan sebelumnya memberikan solusi.
Solusi ini menggunakan metode destruktor sebelum melepaskan objek untuk mencapai tujuan ini. Metode Destructor dapat menghapus semua referensi objek induk internal, yang berarti bagian memori yang meluap dapat dilepaskan.
Berikut adalah kode "setelah perbaikan":
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}function __destruct(){unset($this->bar);}}class Batang {function __construct($foo = null){$this->foo = $foo;}}sementara (benar) {$foo = new Foo();$foo->__destruct();unset($foo);echo number_format(memory_get_usage()) . "n";}?>Perhatikan metode Foo::__destruct() baru dan panggilan ke $foo->__destruct() sebelum melepaskan objek. Sekarang kode ini memecahkan masalah peningkatan penggunaan memori, sehingga kode tersebut dapat bekerja dengan baik.
Solusi kernel PHP?
Mengapa memori meluap terjadi? Saya tidak mahir dalam penelitian kernel PHP, tapi saya yakin masalah ini terkait dengan penghitungan referensi.
Jumlah referensi $foo yang direferensikan dalam $bar tidak akan dikurangi karena objek induk $foo dilepaskan. Saat ini, PHP menganggap Anda masih memerlukan objek $foo, jadi bagian memori ini tidak akan dilepaskan. .atau lebih.
Ketidaktahuan saya benar-benar terlihat di sini, tetapi gagasan umumnya adalah: jumlah referensi tidak dikurangi, sehingga sebagian memori tidak pernah terbebas.
Dalam tautan bug.php.net yang disebutkan di atas, saya melihat bahwa memodifikasi proses pengumpulan sampah akan mengorbankan kinerja yang sangat besar, dan karena saya tidak tahu banyak tentang penghitungan referensi, saya berasumsi ini benar.
Daripada mengubah proses pengumpulan sampah, mengapa tidak unset() melepaskan objek internal? (Atau panggil __destruct() saat melepaskan objek?)
Mungkin pengembang kernel PHP dapat membuat perubahan pada mekanisme pengumpulan sampah ini di sini atau di tempat lain.
Pembaruan: Martin Fjordvald menyebutkan di komentar sebuah tambalan yang ditulis oleh David Wang untuk pengumpulan sampah (sebenarnya lebih terlihat seperti "sepotong kain utuh" - sangat besar. Lihat informasi ekspor CVS di akhir email ini untuk detailnya.) Memang benar ada (email) dan telah mendapat perhatian dari anggota komunitas pengembangan kernel PHP. Pertanyaannya adalah apakah patch ini harus dimasukkan ke dalam PHP5.3 dan belum mendapat banyak dukungan. Saya pikir kompromi yang baik adalah dengan memanggil metode __destruct() pada objek dalam fungsi unset();