PHP8 回收循環(huán)

2023-08-24 14:17 更新

傳統(tǒng)上,像 PHP 之前使用的引用計數(shù)內(nèi)存機(jī)制無法解決循環(huán)引用內(nèi)存泄漏的問題;然而,從 5.3.0 版本開始,PHP 實施了? 引用計數(shù)系統(tǒng)中的同步循環(huán)回收論文中的同步算法來解決這個問題。

對算法的完全說明有點超出這部分內(nèi)容的范圍,將只介紹其中基礎(chǔ)部分。首先,需要確立一些基本規(guī)則。如果 refcount 增加,則該變量仍在使用中,因此不是垃圾。如果 refcount 減少到 0,則 zval 可以釋放。這意味著只有當(dāng)引用計數(shù)參數(shù)減少到非零值時,才能創(chuàng)建垃圾循環(huán)。其次,在垃圾循環(huán)中,可以通過檢查是否可以將 refcount 減少 1,并檢查哪些 zval 的 refcount 為 0 來確定哪些部分是垃圾。

垃圾回收算法 

為避免不得不檢查所有引用計數(shù)可能減少的垃圾循環(huán),這個算法把所有可能根(possible roots 都是zval變量容器),放在根緩沖區(qū)(root buffer)中(用紫色來標(biāo)記,稱為疑似垃圾),這樣可以同時確保每個可能的垃圾根(possible garbage root)在緩沖區(qū)中只出現(xiàn)一次。僅僅在根緩沖區(qū)滿了時,才對緩沖區(qū)內(nèi)部所有不同的變量容器執(zhí)行垃圾回收操作??瓷蠄D的步驟 A。

在步驟 B 中,模擬刪除每個紫色變量。模擬刪除時可能將不是紫色的普通變量引用數(shù)減"1",如果某個普通變量引用計數(shù)變成0了,就對這個普通變量再做一次模擬刪除。每個變量只能被模擬刪除一次,模擬刪除后標(biāo)記為灰(原文說確保不會對同一個變量容器減兩次"1",不對的吧)。

在步驟 C 中,模擬恢復(fù)每個紫色變量?;謴?fù)是有條件的,當(dāng)變量的引用計數(shù)大于0時才對其做模擬恢復(fù)。同樣每個變量只能恢復(fù)一次,恢復(fù)后標(biāo)記為黑,基本就是步驟 B 的逆運(yùn)算。這樣剩下的一堆沒能恢復(fù)的就是該刪除的藍(lán)色節(jié)點了,在步驟 D 中遍歷出來真的刪除掉。

算法中都是模擬刪除、模擬恢復(fù)、真的刪除,都使用簡單的遍歷即可(最典型的深搜遍歷)。復(fù)雜度為執(zhí)行模擬操作的節(jié)點數(shù)正相關(guān),不只是紫色的那些疑似垃圾變量。

對算法的工作原理有了基本的了解后,現(xiàn)在可以回顧一下如何與 PHP 集成。默認(rèn)情況下,PHP 的垃圾回收器是打開的。然而,有個 php.ini 設(shè)置可以進(jìn)行更改:zend.enable_gc。

當(dāng)打開垃圾回收器時,如上所述的循環(huán)查找算法將在根緩沖區(qū)滿時執(zhí)行。根緩沖區(qū)的大小是固定的,可以容納 10,000 個可能的根(盡管可以通過更改 PHP 源代碼中的 Zend/zend_gc.c 中的 GC_THRESHOLD_DEFAULT 常量并重新編譯 PHP 來修改這個值)。當(dāng)關(guān)閉垃圾回收器時,循環(huán)查找算法將永不運(yùn)行。然而,無論是否使用此配置激活垃圾回收機(jī)制,可能根都將始終記錄在根緩沖區(qū)中。

如果在垃圾回收機(jī)制關(guān)閉時,根緩沖區(qū)存滿了可能的根,那么將不會記錄進(jìn)一步的可能根。算法永遠(yuǎn)不會分析那些沒有記錄的可能根。如果他們是循環(huán)引用的一部分,將永不會清除從而導(dǎo)致內(nèi)存泄漏的產(chǎn)生。

即使在垃圾回收機(jī)制不可用時,可能根也被記錄的原因是,相對于每次找到可能根后檢查垃圾回收機(jī)制是否打開而言,記錄可能根的操作更快。不過垃圾回收和分析機(jī)制本身要耗不少時間。

除了改變配置中的 zend.enable_gc 之外,還可以通過調(diào)用 gc_enable() 或 gc_disable() 來啟用/禁用垃圾回收機(jī)制。調(diào)用這些函數(shù)與通過配置打開或關(guān)閉機(jī)制的效果相同。即使可能的根緩沖區(qū)尚未滿,還可以強(qiáng)制回收循環(huán)。為此,可以使用 gc_collect_cycles() 函數(shù)。該函數(shù)將返回算法回收的循環(huán)數(shù)量。

允許打開和關(guān)閉垃圾回收機(jī)制并且允許自主的初始化的原因,是由于你的應(yīng)用程序的某部分可能是高時效性的。在這種情況下,你可能不想使用垃圾回收機(jī)制。當(dāng)然,對你的應(yīng)用程序的某部分關(guān)閉垃圾回收機(jī)制,是在冒著可能內(nèi)存泄漏的風(fēng)險,因為一些可能根也許存不進(jìn)有限的根緩沖區(qū)。因此,就在你調(diào)用gc_disable()函數(shù)釋放內(nèi)存之前,先調(diào)用gc_collect_cycles()函數(shù)可能比較明智。因為這將清除已存放在根緩沖區(qū)中的所有可能根,然后在垃圾回收機(jī)制被關(guān)閉時,可留下空緩沖區(qū)以有更多空間存儲可能根。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號