PHP垃圾回收之性能

jopen 12年前發布 | 26K 次閱讀 PHP PHP開發

關于PHP垃圾回收機制(Garbage Collection . GC) ,原作者寫了三篇文章。
第一篇,主要講解PHP如何處理變量
第二篇主要講常用的GC方法,以及GC是如何實現的
這是第三篇,主要從性能方面來考慮垃圾回收。

原文: http://derickrethans.nl/collecting-garbage-performance-considerations.html

在上二篇文章中提到了circular reference(環形引用)以及如何對其進行垃圾回收,
不可否認,垃圾回收的過程消耗了一定的時間,會對性能產生一定的影響。
上一篇文章說到把數組變量的zval容量收集到root buffer的速度還是挺快的,
其實這是因為php5.3 runtime有了一些改進,使得這個收集的過程對整體性能影響不大。

提到一種算法,我們一般都會考慮到它使用的空間與時間。
PHP垃圾回收機制對性能的影響,也從這兩個方面來看:
一是減少了多少內存占用;二是額外消耗了多少時間。

垃圾回收機制可以減少多少內存占用

當root buffer充滿的時候(默認需要一萬個zval容器),或者當gc_collect_cyles()被調用的時候,PHP會啟動垃圾回收。
下面的圖表,顯示了一段代碼在php5.2(沒有垃圾回收)與php5.3(有垃圾回收)環境下運行時占用的內存。
代碼如下:

<?php
class Foo
{
        public $var = '3.1415962654';
}

$baseMemory = memory_get_usage();

for ( $i = 0; $i <= 100000; $i++ )
{
        $a = new Foo;
        $a->self = $a;
        if ( $i % 500 === 0 )
        {
                echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "\n";
        }
}
?>
PHP垃圾回收之性能
在這段代碼中,我們創建了一個對象$a,然后讓$a的屬性self指向了$a。
當$a在下一次循環中被重新new出來的時候,內存泄露就出現了。
如果這里不太理解,請回去看第一篇文件《 PHP垃圾回收機制 之 變量的處理
在這種情況下,有二個zval容器所占用的空間無法回收,一個是$a本身,另外一個是$a->var。
但是只有$a的zval會被收集到root buffer中。
當root buffer收集到10000個zval的時候,垃圾回收啟動,清理掉這10000個zval,以及它們的子zval。一共就清理掉了20000個zval。
在圖中可以看得很清楚,每當達到10000次循環,內存的占用量就減少一次。
最終可以看到,在PHP5.3中內存最多占用為9M,而在php5.2中,內存占用為90M。
如果程序繼續循環下去,php5.3中內存還是最多占用9M,在php5.2中,內存占用會一直增長上去。

垃圾回收機制會額外消耗多少時間

我們把上面的PHP代碼修改一下,讓它循環更多次。

<?php
class Foo
{
        public $var = '3.1415962654';
}

for ( $i = 0; $i <= 1000000; $i++ )
{
        $a = new Foo;
        $a->self = $a;
}

echo memory_get_peak_usage(), "\n";
?>

接下來運行這段代碼二次。一次關閉gc,另外一次打開gc。

time ~/dev/php/php-5.3dev/sapi/cli/php -dzend.enable_gc=0 \
    -dmemory_limit=-1 -n part3-example2.php
# and
time ~/dev/php/php-5.3dev/sapi/cli/php -dzend.enable_gc=1 \
    -dmemory_limit=-1 -n part3-example2.php

在我的測試機上,第一條命令使用了10.7秒,第二條命令使用了11.4秒,大約多使用了7%的時間。
內存占用上,第一次為931M,第二次10M,節省了98%。
這次的評測雖然并不是很科學,或者也不能代表實際應用中的情況,但從中還是可以很明顯地看到garbage collection對于內存的節省。
如果循環更多次,時間的額外消耗仍然會是7%,但是內存的節省就可能會是99%了,甚至會很趨近于100%。

php內部的garbage collection 統計數據

php源代碼中已經有一定的gc統計功能了,不過默認是關閉著的。
如果要使用這個統計功能,就要重新編譯PHP:

export CFLAGS=GC_BENCH=1
./config.nice
make clean
make

然后運行上面的例子代碼,運行結束時,就可以看到如下的信息了:

GC Statistics
-------------
Runs:               110
Collected:          2072204
Root buffer length: 0
Root buffer peak:   10000

      Possible            Remove from  Marked
        Root    Buffered     buffer     grey
      --------  --------  -----------  ------
ZVAL   7175487   1491291    1241690   3611871
ZOBJ  28506264   1527980     677581   1025731

上面的信息說明垃圾回收運行了110次,累計回收了2M左右的內存,root buffer的峰值為10000。這個是顯然的。

結論

在這最后一篇文章里面,我們簡單看了一下php5.3中垃圾回收對于PHP性能的影響。
在一般情況下,只有真正在進行垃圾回收的時候,才會額外消耗一部分時間。
所以如果代碼運行時間較短,垃圾回收甚至都不會運行,對性能也不會產生什么影響。

如果垃圾回收真的運行了,那么它節省下來的內存,也可以使得web服務器同時運行更多的程序。

垃圾回收真正發揮自己優勢的地方在于長時間運行的腳本,比如測試用例啊,或者守護程序啊,或者php-gtk的界面程序之類的。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!