PHP中的循環(huán)引用是記憶體洩漏的常見原因。當(dāng)物件直接或間接地相互引用時(shí),就會(huì)發(fā)生循環(huán)引用。幸運(yùn)的是,PHP有一個(gè)垃圾收集器可以偵測和清理循環(huán)引用。但是,這會(huì)消耗CPU週期並可能減慢應(yīng)用程式的速度。
垃圾收集器會(huì)在記憶體中存在10,000個(gè)可能的循環(huán)物件或陣列,並且其中一個(gè)超出作用域時(shí)觸發(fā)。
如果您有少量使用大量記憶體的對(duì)象,則永遠(yuǎn)不會(huì)觸發(fā)垃圾收集。即使記憶體被垃圾收集器應(yīng)該收集的孤立物件使用,您也可能達(dá)到記憶體限制。
這就是為什麼您應(yīng)該識(shí)別建立循環(huán)引用的情況並避免它們的原因。
理想情況下,對(duì)於Web應(yīng)用程序,您希望停用垃圾收集器,並在發(fā)送回應(yīng)後讓PHP釋放所有記憶體。但這對(duì)於長時(shí)間運(yùn)行的腳本(例如守護(hù)進(jìn)程或工作進(jìn)程)來說是危險(xiǎn)的,因?yàn)橛洃涹w洩漏會(huì)隨著時(shí)間的推移而累積,並透過頻繁調(diào)用垃圾收集器來減慢應(yīng)用程式的速度。
在本文中,我們將探討閉包和產(chǎn)生器如何保存循環(huán)引用以及如何防止它們。
- 關(guān)於循環(huán)引用
- 循環(huán)引用的典型範(fàn)例
- 使用弱引用防止循環(huán)引用
- 閉包與循環(huán)引用
- 產(chǎn)生器與循環(huán)引用
- 結(jié)論
關(guān)於循環(huán)引用
循環(huán)引用的典型範(fàn)例
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { public function __construct(public A $a) {} }
在此範(fàn)例中,A和B相互引用。當(dāng)您建立A的實(shí)例時(shí),它會(huì)建立一個(gè)引用A的B實(shí)例。這會(huì)建立一個(gè)循環(huán)引用。
為了偵測循環(huán)引用,我們可以使用gc_collect_cycles()
手動(dòng)觸發(fā)垃圾收集器,並使用gc_status()
讀取收集到的引用的數(shù)量。
// 創(chuàng)建的對(duì)象但未分配給變量 new A(); gc_collect_cycles(); print_r(gc_status());
這將輸出:
<code>Array ( ... [collected] => 2 ... )</code>
此範(fàn)例顯示垃圾收集器已偵測到並刪除了2個(gè)具有循環(huán)引用的物件。
您也可以使用xdebug_debug_zval()
函數(shù)查看物件的參考數(shù)量。
使用弱引用防止循環(huán)引用
當(dāng)遇到循環(huán)引用時(shí),一個(gè)簡單的解決方案是使用弱引用。弱引用是一個(gè)對(duì)象,它所持有的引用不會(huì)阻止垃圾收集器收集它所引用的對(duì)象。在PHP中,您可以使用WeakReference
類別建立弱引用。
這需要對(duì)程式碼進(jìn)行一些更改。 B類現(xiàn)在儲(chǔ)存WeakReference
物件而不是A物件。您必須使用WeakReference
物件的get()
方法存取A物件。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { /** @var WeakReference<a> $a */ public WeakReference $a; public function __construct(A $a) { $this->a = WeakReference::create($a); } }
// 創(chuàng)建的對(duì)象但未分配給變量 new A(); gc_collect_cycles(); print_r(gc_status()); // [collected] => 0
在輸出中,您將看到收集到的引用數(shù)量現(xiàn)在為0。
提示1:僅在必要時(shí)使用弱引用來防止循環(huán)引用。
閉包與循環(huán)引用
PHP中閉包的概念是建立一個(gè)可以存取父作用域中變數(shù)的函數(shù)。如果您不小心,這可能會(huì)導(dǎo)致循環(huán)引用。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { public function __construct(public A $a) {} }
在此範(fàn)例中,閉包$a->b
引用父作用域中的變數(shù)$a
。循環(huán)引用很容易發(fā)現(xiàn),因?yàn)橐檬敲鞔_的。
但是,如果您使用閉包的簡寫語法,則可能會(huì)以更隱藏的方式出現(xiàn)相同的問題。使用箭頭函數(shù),變數(shù)$a
不會(huì)在閉包中明確引用,但它仍然被按引用捕獲。
// 創(chuàng)建的對(duì)象但未分配給變量 new A(); gc_collect_cycles(); print_r(gc_status());
在此範(fàn)例中,收集到的引用數(shù)量為2,表示有循環(huán)引用。
閉包中對(duì)$this的引用
在類別方法中建立的任何非靜態(tài)閉包都會(huì)對(duì)物件實(shí)例($this
)具有引用,即使沒有存取$this
也是如此。
<code>Array ( ... [collected] => 2 ... )</code>
這是因?yàn)?code>$this引用總是在閉包中按引用捕獲??梢允褂?code>Reflection::getClosureThis()存取它。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { /** @var WeakReference<a> $a */ public WeakReference $a; public function __construct(A $a) { $this->a = WeakReference::create($a); } }
如果從全域作用域或靜態(tài)方法建立閉包,則$this
引用為null。
提示2:如果您不需要
$this
,請(qǐng)務(wù)必使用static function () {}
或static fn () =>
來建立閉包。
產(chǎn)生器與循環(huán)引用
我們來談?wù)勥@篇文章的原因。我最近發(fā)現(xiàn)了一些東西: 生成器會(huì)保留引用,只要它們沒有被耗盡。
在此範(fàn)例中,該類別將生成器儲(chǔ)存在一個(gè)屬性中,但生成器對(duì)物件實(shí)例具有$this
引用。
生成器表現(xiàn)得像一個(gè)閉包,並保留對(duì)物件實(shí)例的參考。
// 創(chuàng)建的對(duì)象但未分配給變量 new A(); gc_collect_cycles(); print_r(gc_status()); // [collected] => 0
類別實(shí)例被垃圾收集器收集,因?yàn)樗鼘?duì)生成器有引用,而生成器對(duì)物件實(shí)例有引用。
一旦生成器被耗盡,參考就會(huì)被釋放,物件實(shí)例就會(huì)從記憶體中刪除。
function createCircularReference() { $a = new stdClass(); $a->b = function () use ($a) { return $a; }; return $a; }
提示3:透過迭代始終耗盡生成器。
提示4:使用靜態(tài)方法或閉包來建立生成器,以免保留物件實(shí)例的參考。
結(jié)論
循環(huán)引用是PHP中記憶體洩漏的常見原因。即使垃圾收集器可以偵測和清理循環(huán)引用,它也會(huì)消耗CPU週期並可能減慢應(yīng)用程式的速度。您必須檢測創(chuàng)建此類循環(huán)引用的情況並調(diào)整程式碼以防止它們。使用弱引用可以防止循環(huán)引用,但一些簡單的技巧可以幫助您首先防止循環(huán)引用:
- 如果不需要
$this
,則使用static function () {}
或static fn () =>
來建立閉包。 - 透過迭代始終耗盡生成器。
- 使用靜態(tài)方法或閉包來建立生成器,以免保留物件實(shí)例的參考。
閱讀更多
- PHP垃圾收集-效能注意事項(xiàng)
- PHP中的垃圾收集是什麼?如何充分利用它?
- memprof——PHP的記憶體分析器。協(xié)助查找PHP腳本中的記憶體洩漏。
- Xdebug的內(nèi)建分析器
以上是PHP 閉包和生成器可以保存循環(huán)引用的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級(jí)程式碼編輯軟體(SublimeText3)

PHP變量作用域常見問題及解決方法包括:1.函數(shù)內(nèi)部無法訪問全局變量,需使用global關(guān)鍵字或參數(shù)傳入;2.靜態(tài)變量用static聲明,只初始化一次並在多次調(diào)用間保持值;3.超全局變量如$_GET、$_POST可在任何作用域直接使用,但需注意安全過濾;4.匿名函數(shù)需通過use關(guān)鍵字引入父作用域變量,修改外部變量則需傳遞引用。掌握這些規(guī)則有助於避免錯(cuò)誤並提升代碼穩(wěn)定性。

要安全處理PHP文件上傳需驗(yàn)證來源與類型、控製文件名與路徑、設(shè)置服務(wù)器限制並二次處理媒體文件。 1.驗(yàn)證上傳來源通過token防止CSRF並通過finfo_file檢測真實(shí)MIME類型使用白名單控制;2.重命名文件為隨機(jī)字符串並根據(jù)檢測類型決定擴(kuò)展名存儲(chǔ)至非Web目錄;3.PHP配置限制上傳大小及臨時(shí)目錄Nginx/Apache禁止訪問上傳目錄;4.GD庫重新保存圖片清除潛在惡意數(shù)據(jù)。

PHP註釋代碼常用方法有三種:1.單行註釋用//或#屏蔽一行代碼,推薦使用//;2.多行註釋用/.../包裹代碼塊,不可嵌套但可跨行;3.組合技巧註釋如用/if(){}/控制邏輯塊,或配合編輯器快捷鍵提升效率,使用時(shí)需注意閉合符號(hào)和避免嵌套。

AgeneratorinPHPisamemory-efficientwaytoiterateoverlargedatasetsbyyieldingvaluesoneatatimeinsteadofreturningthemallatonce.1.Generatorsusetheyieldkeywordtoproducevaluesondemand,reducingmemoryusage.2.Theyareusefulforhandlingbigloops,readinglargefiles,or

寫好PHP註釋的關(guān)鍵在於明確目的與規(guī)範(fàn),註釋應(yīng)解釋“為什麼”而非“做了什麼”,避免冗餘或過於簡單。 1.使用統(tǒng)一格式,如docblock(/*/)用於類、方法說明,提升可讀性與工具兼容性;2.強(qiáng)調(diào)邏輯背後的原因,如說明為何需手動(dòng)輸出JS跳轉(zhuǎn);3.在復(fù)雜代碼前添加總覽性說明,分步驟描述流程,幫助理解整體思路;4.合理使用TODO和FIXME標(biāo)記待辦事項(xiàng)與問題,便於後續(xù)追蹤與協(xié)作。好的註釋能降低溝通成本,提升代碼維護(hù)效率。

ToinstallPHPquickly,useXAMPPonWindowsorHomebrewonmacOS.1.OnWindows,downloadandinstallXAMPP,selectcomponents,startApache,andplacefilesinhtdocs.2.Alternatively,manuallyinstallPHPfromphp.netandsetupaserverlikeApache.3.OnmacOS,installHomebrew,thenrun'bre

在PHP中獲取字符串特定索引字符可用方括號(hào)或花括號(hào),但推薦方括號(hào);索引從0開始,超出範(fàn)圍訪問返回空值,不可賦值;處理多字節(jié)字符需用mb_substr。例如:$str="hello";echo$str[0];輸出h;而中文等字符需用mb_substr($str,1,1)獲取正確結(jié)果;實(shí)際應(yīng)用中循環(huán)訪問前應(yīng)檢查字符串長度,動(dòng)態(tài)字符串需驗(yàn)證有效性,多語言項(xiàng)目建議統(tǒng)一使用多字節(jié)安全函數(shù)。

易於效率,啟動(dòng)啟動(dòng)tingupalocalserverenverenvirestoolslikexamppandacodeeditorlikevscode.1)installxamppforapache,mysql,andphp.2)uscodeeditorforsyntaxssupport.3)
