顯示廣告
隱藏 ✕
※ 本文為 dinos 轉寄自 ptt.cc 更新時間: 2013-06-04 11:41:23
看板 PHP
作者 rickysu (Ricky)
標題 Re: [分享] PHP GC 機制
時間 Tue Jun  4 09:32:44 2013


※ 引述《rickysu (Ricky)》之銘言:
: 題外話: 搞了好久終於註冊好 PTT 了。
: ======================================
: 前陣子看到某篇文章提到,要回收物件時,
: 使用 $obj = null 會馬上回收,unset($obj) 則會比較慢。
: 在解答之前,先來玩個小遊戲。
: <?php
: class test
: {
:     public function __destruct()
:     {
:         echo "object of test is dead\n";
:     }
: }
: $test = new test();
: $test = null;
: die("program is end\n");
: 執行結果
: object of test is dead
: program is end
: 很符合結果 $test = null 先執行 GC (destruct) 接著 die output
: ===================
: <?php
: class test
: {
:     protected $me;
:     public function __contruct()
:     {
:         $this-> me = $this;
:     }
:     public function __destruct()
:     {
:         echo "object of test is dead\n";
:     }
: }
: $test = new test();
: unset($test);
: die("program is end\n");
: 執行結果
: program is end
: object of test is dead
: unset 沒有進行 GC,一直到程式結束後,才開始進行 GC。
: 疑 unset 沒有進行 GC ??!!
: 好玩的事情發生了,難道真的是 unset 是看心情 GC 的??
: 其實上面的範例即使改成 $test = null; 執行結果也是一樣的。
: 這邊就先賣個關子,明天再來解答為什麼會有這個結果,以及該怎麼避免這樣的問題。

PHP 在判斷物件是否該被 GC 啟用了 reference counting 的機制來作為判斷。
簡單的說,當某個物件被參照時就把他的 refcount+1 。
例如 $a = new test();
他是把 test 物件 refcount + 1,
$a 只是一個指向 test 物件的指標。
如果這時候又有一個
$b = $a;
php 底層則是把 test 物件的 refcount 再度 + 1。
這也就是為什麼 php 從 5.0 之後物件都是 by referenc 的原因。
一方面為了節省記憶體,一方面則是加快物件引用的處理速度。

那 PHP 怎麼判斷一個物件該被執行 GC。
當一個變數被賦予新的數值,或是被 unset ,就會把他對應物件的 refcount-1。
當物件的 refcount 歸 0 時,進行 GC (先執行 destructor,再將 memory 回收)。

看起來 reference counting 的機制運作得很完美,
但是某天某位仁兄在 PHP 的 bug report 中發了一個 bug。大致的問題是這樣。

他發覺當某個情況時 PHP 會迅速的吃光所有的 memory。
(註: PHP 底層處理 array 跟 object 使用的機制是相同的,都是透過 hashtable )

while(true){
    $a = array(1,2,3,4);
    $a[] = &$a;
}

照理來說 $a 被重新 assign array 時,原本的 memory 應該要被釋放掉才對。
可是實際狀況卻不是這樣。 Why??

我們來看一下前面提到的第二個例子
$test = new test();
首先 constructor 中指定了
$this->me = $this; 這時候 test 物件的 refcount => 1
接著 $test = new test();  refcount = 2

然後執行 unset($test);   refcount = 1,因為參照的變數被 unset 所以 -1

這時候好玩的事情發生了 refcount 尚未歸 0,所以不會被 GC ,
但是已經沒有任何變數參照到這個物件了。
這個物件就永遠變成垃圾而且無法被回收,除非程式結束才會被回收。

那這個問題在 PHP 5.2 以前的版本是無解的,幸好 PHP 大部分的狀況都是一次 request
後就結束,程式結束後 memory 還是會被 OS 回收。

但是隨著 framework 的盛行,
PHP 中物件被環形引用($a->next = $b; $b->next = $a;)的狀況越來越多,
因此 PHP 5.3 開始重新設計整個 GC 機制。
如果有仔細看過 PHP 5.3 的 release note ,除了 namespace 之外
最重大的變動就是多了 gc_collect_cycles, gc_enable, gc_disable。

PHP 5.3 中引入了一個新的演算法,專們用來對付這種環形引用所留下來的垃圾。
如果想看看他是怎運作的可以參考這篇
http://www.php.net/manual/en/features.gc.collecting-cycles.php

不過這種演算法每次都得遞迴整個引用的物件,
造成的代價相當高。(新版本跑得比之前版本慢是不被允許的)

因此PHP 5.3 會將每個可能需要被 GC 的物件先丟到一個 list 中。
這邊所謂的 "可能需要被GC" 是指,只要是物件或是array,
當他被 unreference 時就會先丟到這個暫存 list 中。
當 list 滿的時候一次進行 GC (印象中沒記錯list只能容納1000個待GC的物件)。

如果想要強制進行 GC 只要呼叫 gc_collect_cycles() ,就會馬上進行 GC。

希望這篇文章能讓大家對 PHP 的 GC 執行時機有些幫助。



--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.130.136.115

--
※ 同主題文章:
※ 看板: dinos 文章推薦值: 0 目前人氣: 0 累積人氣: 91 
作者 rickysu 的最新發文:
  • +3 Re: [請益] 用迴圈跑出報名表 - PHP 板
    作者: 220.130.136.115 (台灣) 2013-08-16 09:28:04
    幫你把 Code 整理一下吧... 順便把一些壞習慣改掉 <?php //sql injection $str="select paperid,papername,paperman f …
    4F 3推
  • +2 [教學] OR || AND && 不能亂用 - PHP 板
    作者: 220.130.136.115 (台灣) 2013-07-18 10:46:53
    這篇算是亂入文 XD 回歸正題,在前幾篇的討論中有人使用到 And 這個 operator。 然而在 php 中 And 跟 && 是不同的東西,別混用了。 舉個範例 <?php …
    2F 2推
  • Re: [分享] PHP GC 機制 - PHP 板
    作者: 220.130.136.115 (台灣) 2013-06-04 09:32:44
    PHP 在判斷物件是否該被 GC 啟用了 reference counting 的機制來作為判斷。 簡單的說,當某個物件被參照時就把他的 refcount+1 。 例如 $a = new test() …
  • [分享] PHP GC 機制 - PHP 板
    作者: 220.130.136.115 (台灣) 2013-06-03 18:52:00
    題外話: 搞了好久終於註冊好 PTT 了。 ====================================== 前陣子看到某篇文章提到,要回收物件時, 使用 $obj = null 會馬上回 …
  • Re: [請益] 兩個 socket server 之間的溝通? - PHP 板
    作者: 220.130.136.115 (台灣) 2013-06-03 17:29:17
    要 keep 大量 connection,以及快速 response , 使用 socket 函數幾乎是不可行的方式。 傳統的 socket select,底層還是透過 polling 方式去監視 s …
分享網址: 複製 已複製
guest
x)推文 r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇