PHP中的垃圾回收(Garbage Collection,簡(jiǎn)稱GC)是保證程序運(yùn)行效率和可靠性的必要機(jī)制之一。GC是自動(dòng)的,不需開(kāi)發(fā)者顯式地調(diào)用,它會(huì)自動(dòng)檢測(cè)和清除不再使用的內(nèi)存、變量和對(duì)象,確保程序不會(huì)因?yàn)槌錆M無(wú)用對(duì)象的內(nèi)存而耗盡系統(tǒng)資源。在本文中,我們將深入探討PHP GC的原理。
在PHP中,每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)器(Reference Counter,簡(jiǎn)稱RefCnt),保存了指向該對(duì)象的指針數(shù)目。當(dāng)一個(gè)新的變量引用該對(duì)象時(shí),該對(duì)象的RefCnt加1,當(dāng)一個(gè)變量的引用失效時(shí)(比如指向null或超出作用域),該對(duì)象的引用計(jì)數(shù)減1,當(dāng)該對(duì)象的引用計(jì)數(shù)為0時(shí),系統(tǒng)會(huì)認(rèn)為該對(duì)象不再被使用,將其內(nèi)存回收。
class MyClass { public $name; function __construct($name) { $this->name = $name; } } $a = new MyClass("Hello"); $b = $a; $c = $a; unset($a); $c = null;
以上例子中,當(dāng)$b和$c指向同一對(duì)象時(shí),該對(duì)象的RefCnt為2,當(dāng)$a變量被unset時(shí),對(duì)象的RefCnt減1,變?yōu)?。當(dāng)$c指向null時(shí),對(duì)象的RefCnt再次減1,變?yōu)?,該對(duì)象的內(nèi)存會(huì)被GC回收。
PHP提供了三種GC策略:引用計(jì)數(shù)(Refcounting)、標(biāo)記-清除(Mark-and-Sweep)和標(biāo)記-壓縮(Mark-and-Compact)。
引用計(jì)數(shù)是PHP默認(rèn)的GC策略,它簡(jiǎn)單而高效,但存在某些情況下可能導(dǎo)致內(nèi)存泄漏的問(wèn)題。例如:
$a = new MyClass("ABC"); $a->child = new MyClass("XYZ"); $a->child->parent = $a; unset($a);
該例中,$a對(duì)象和$a->child對(duì)象相互引用,它們的RefCnt都為2,當(dāng)unset($a)執(zhí)行完成后,$a對(duì)象的RefCnt變?yōu)?,但$a->child的RefCnt還是1,這是因?yàn)?a->child->parent指向它本身,導(dǎo)致對(duì)象無(wú)法釋放。
為避免在這種場(chǎng)景下出現(xiàn)內(nèi)存泄漏,PHP引入了標(biāo)記-清除策略。該策略會(huì)定期地檢測(cè)所有變量、對(duì)象和數(shù)組,找出無(wú)法訪問(wèn)的引用,并將它們標(biāo)記為垃圾待清理。
標(biāo)記-清除策略的缺點(diǎn)是清除過(guò)程中會(huì)產(chǎn)生大量碎片化的內(nèi)存空間,從而導(dǎo)致內(nèi)存不連續(xù),影響了系統(tǒng)的性能。因此,PHP還支持標(biāo)記-壓縮策略。該策略在GC的過(guò)程中不僅會(huì)清除無(wú)用對(duì)象,還會(huì)對(duì)內(nèi)存空間做整理和壓縮,使內(nèi)存空間更加連續(xù),提高程序運(yùn)行效率。
總之,PHP GC的選擇取決于應(yīng)用的實(shí)際情況。引用計(jì)數(shù)是一種快速、簡(jiǎn)單的策略,但無(wú)法處理相互依賴的對(duì)象。標(biāo)記-清除策略解決了這個(gè)問(wèn)題,但可能導(dǎo)致內(nèi)存碎片化。標(biāo)記-壓縮策略不僅避免了內(nèi)存碎片,還能提高程序效率,但需要更多的CPU時(shí)間和內(nèi)存空間。