成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺談PHP 5中垃圾回收算法的演化

開發(fā) 前端 后端 算法
本文將分別討論PHP5.2和PHP5.3的垃圾回收機制,并討論這種演化和改進對于程序員編寫PHP的影響以及要注意的問題。

PHP是一門托管型語言,在PHP編程中程序員不需要手工處理內(nèi)存資源的分配與釋放(使用C編寫PHP或Zend擴展除外),這就意味著PHP本身實現(xiàn)了垃圾回收機制(Garbage Collection)。現(xiàn)在如果去PHP官方網(wǎng)站(php.net)可以看到,目前PHP5的兩個分支版本PHP5.2和PHP5.3是分別更新的,這是因為許多項目仍然使用5.2版本的PHP,而5.3版本對5.2并不是完全兼容。PHP5.3在PHP5.2的基礎(chǔ)上做了諸多改進,其中垃圾回收算法就屬于一個比較大的改變。本文將分別討論PHP5.2和PHP5.3的垃圾回收機制,并討論這種演化和改進對于程序員編寫PHP的影響以及要注意的問題。

PHP變量及關(guān)聯(lián)內(nèi)存對象的內(nèi)部表示

垃圾回收說到底是對變量及其所關(guān)聯(lián)內(nèi)存對象的操作,所以在討論PHP的垃圾回收機制之前,先簡要介紹PHP中變量及其內(nèi)存對象的內(nèi)部表示(其C源代碼中的表示)。

PHP官方文檔中將PHP中的變量劃分為兩類:標(biāo)量類型和復(fù)雜類型。標(biāo)量類型包括布爾型、整型、浮點型和字符串;復(fù)雜類型包括數(shù)組、對象和資源;還有一個NULL比較特殊,它不劃分為任何類型,而是單獨成為一類。

所有這些類型,在PHP內(nèi)部統(tǒng)一用一個叫做zval的結(jié)構(gòu)表示,在PHP源代碼中這個結(jié)構(gòu)名稱為“_zval_struct”。zval的具體定義在PHP源代碼的“Zend/zend.h”文件中,下面是相關(guān)代碼的摘錄。

  1. typedef union _zvalue_value {  
  2.     long lval;                  /* long value */ 
  3.     double dval;                /* double value */ 
  4.     struct {  
  5.         char *val;  
  6.         int len;  
  7.     } str;  
  8.     HashTable *ht;              /* hash table value */ 
  9.     zend_object_value obj;  
  10. } zvalue_value;  
  11.  
  12. struct _zval_struct {  
  13.     /* Variable information */ 
  14.     zvalue_value value;       
  15. /* value */ 
  16.     zend_uint refcount__gc;  
  17.     zend_uchar type;    /* active type */ 
  18.     zend_uchar is_ref__gc;  
  19. }; 

其中聯(lián)合體“_zvalue_value”用于表示PHP中所有變量的值,這里之所以使用union,是因為一個zval在一個時刻只能表示一種類型的變量。可以看到_zvalue_value中只有5個字段,但是PHP中算上NULL有8種數(shù)據(jù)類型,那么PHP內(nèi)部是如何用5個字段表示8種類型呢?這算是PHP設(shè)計比較巧妙的一個地方,它通過復(fù)用字段達到了減少字段的目的。例如,在PHP內(nèi)部布爾型、整型及資源(只要存儲資源的標(biāo)識符即可)都是通過lval字段存儲的;dval用于存儲浮點型;str存儲字符串;ht存儲數(shù)組(注意PHP中的數(shù)組其實是哈希表);而obj存儲對象類型;如果所有字段全部置為0或NULL則表示PHP中的NULL,這樣就達到了用5個字段存儲8種類型的值。

而當(dāng)前zval中的value(value的類型即是_zvalue_value)到底表示那種類型,則由“_zval_struct”中的type確定。_zval_struct即是zval在C語言中的具體實現(xiàn),每個zval表示一個變量的內(nèi)存對象。除了value和type,可以看到_zval_struct中還有兩個字段refcount__gc和is_ref__gc,從其后綴就可以斷定這兩個家伙與垃圾回收有關(guān)。沒錯,PHP的垃圾回收全靠這倆字段了。其中refcount__gc表示當(dāng)前有幾個變量引用此zval,而is_ref__gc表示當(dāng)前zval是否被按引用引用,這話聽起來很拗口,這和PHP中zval的“Write-On-Copy”機制有關(guān),由于這個話題不是本文重點,因此這里不再詳述,讀者只需記住refcount__gc這個字段的作用即可。

PHP5.2中的垃圾回收算法——Reference Counting

PHP5.2中使用的內(nèi)存回收算法是大名鼎鼎的Reference Counting,這個算法中文翻譯叫做“引用計數(shù)”,其思想非常直觀和簡潔:為每個內(nèi)存對象分配一個計數(shù)器,當(dāng)一個內(nèi)存對象建立時計數(shù)器初始化為1(因此此時總是有一個變量引用此對象),以后每有一個新變量引用此內(nèi)存對象,則計數(shù)器加1,而每當(dāng)減少一個引用此內(nèi)存對象的變量則計數(shù)器減1,當(dāng)垃圾回收機制運作的時候,將所有計數(shù)器為0的內(nèi)存對象銷毀并回收其占用的內(nèi)存。而PHP中內(nèi)存對象就是zval,而計數(shù)器就是refcount__gc。

例如下面一段PHP代碼演示了PHP5.2計數(shù)器的工作原理(計數(shù)器值通過xdebug得到):

  1. <?php  
  2.  
  3. $val1 = 100; //zval(val1).refcount_gc = 1;  
  4. $val2 = $val1//zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(因為是Write on copy,當(dāng)前val2與val1共同引用一個zval)  
  5. $val2 = 200; //zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此處val2新建了一個zval)  
  6. unset($val1); //zval(val1).refcount_gc = 0($val1引用的zval再也不可用,會被GC回收)  
  7.  
  8. ?> 

Reference Counting簡單直觀,實現(xiàn)方便,但卻存在一個致命的缺陷,就是容易造成內(nèi)存泄露。很多朋友可能已經(jīng)意識到了,如果存在循環(huán)引用,那么Reference Counting就可能導(dǎo)致內(nèi)存泄露。例如下面的代碼:

  1. <?php  
  2.  
  3. $a = array();  
  4. $a[] = & $a;  
  5. unset($a);  
  6.  
  7. ?> 

這段代碼首先建立了數(shù)組a,然后讓a的第一個元素按引用指向a,這時a的zval的refcount就變?yōu)?,然后我們銷毀變量a,此時a最初指向的zval的refcount為1,但是我們再也沒有辦法對其進行操作,因為其形成了一個循環(huán)自引用,如下圖所示:

淺談PHP5中垃圾回收算法(Garbage Collection)的演化

其中灰色部分表示已經(jīng)不復(fù)存在。由于a之前指向的zval的refcount為1(被其HashTable的第一個元素引用),這個zval就不會被GC銷毀,這部分內(nèi)存就泄露了。

這里特別要指出的是,PHP是通過符號表(Symbol Table)存儲變量符號的,全局有一個符號表,而每個復(fù)雜類型如數(shù)組或?qū)ο笥凶约旱姆柋恚虼松厦娲a中,a和a[0]是兩個符號,但是a儲存在全局符號表中,而a[0]儲存在數(shù)組本身的符號表中,且這里a和a[0]引用同一個zval(當(dāng)然符號a后來被銷毀了)。希望讀者朋友注意分清符號(Symbol)的zval的關(guān)系。

在PHP只用于做動態(tài)頁面腳本時,這種泄露也許不是很要緊,因為動態(tài)頁面腳本的生命周期很短,PHP會保證當(dāng)腳本執(zhí)行完畢后,釋放其所有資源。但是PHP發(fā)展到目前已經(jīng)不僅僅用作動態(tài)頁面腳本這么簡單,如果將PHP用在生命周期較長的場景中,例如自動化測試腳本或deamon進程,那么經(jīng)過多次循環(huán)后積累下來的內(nèi)存泄露可能就會很嚴重。這并不是我在聳人聽聞,我曾經(jīng)實習(xí)過的一個公司就通過PHP寫的deamon進程來與數(shù)據(jù)存儲服務(wù)器交互。

由于Reference Counting的這個缺陷,PHP5.3改進了垃圾回收算法。

PHP5.3中的垃圾回收算法——Concurrent Cycle Collection in Reference Counted Systems

PHP5.3的垃圾回收算法仍然以引用計數(shù)為基礎(chǔ),但是不再是使用簡單計數(shù)作為回收準則,而是使用了一種同步回收算法,這個算法由IBM的工程師在論文Concurrent Cycle Collection in Reference Counted Systems中提出。

這個算法可謂相當(dāng)復(fù)雜,從論文29頁的數(shù)量我想大家也能看出來,所以我不打算(也沒有能力)完整論述此算法,有興趣的朋友可以閱讀上面的提到的論文(強烈推薦,這篇論文非常精彩)。

我在這里,只能大體描述一下此算法的基本思想。

首先PHP會分配一個固定大小的“根緩沖區(qū)”,這個緩沖區(qū)用于存放固定數(shù)量的zval,這個數(shù)量默認是10,000,如果需要修改則需要修改源代碼Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES然后重新編譯。

由上文我們可以知道,一個zval如果有引用,要么被全局符號表中的符號引用,要么被其它表示復(fù)雜類型的zval中的符號引用。因此在zval中存在一些可能根(root)。這里我們暫且不討論PHP是如何發(fā)現(xiàn)這些可能根的,這是個很復(fù)雜的問題,總之PHP有辦法發(fā)現(xiàn)這些可能根zval并將它們投入根緩沖區(qū)。

當(dāng)根緩沖區(qū)滿額時,PHP就會執(zhí)行垃圾回收,此回收算法如下:

1、對每個根緩沖區(qū)中的根zval按照深度優(yōu)先遍歷算法遍歷所有能遍歷到的zval,并將每個zval的refcount減1,同時為了避免對同一zval多次減1(因為可能不同的根能遍歷到同一個zval),每次對某個zval減1后就對其標(biāo)記為“已減”。

2、再次對每個緩沖區(qū)中的根zval深度優(yōu)先遍歷,如果某個zval的refcount不為0,則對其加1,否則保持其為0。

3、清空根緩沖區(qū)中的所有根(注意是把這些zval從緩沖區(qū)中清除而不是銷毀它們),然后銷毀所有refcount為0的zval,并收回其內(nèi)存。

如果不能完全理解也沒有關(guān)系,只需記住PHP5.3的垃圾回收算法有以下幾點特性:

1、并不是每次refcount減少時都進入回收周期,只有根緩沖區(qū)滿額后在開始垃圾回收。

2、可以解決循環(huán)引用問題。

3、可以總將內(nèi)存泄露保持在一個閾值以下。

PHP5.2與PHP5.3垃圾回收算法的性能比較

由于我目前條件所限,我就不重新設(shè)計試驗了,而是直接引用PHP Manual中的實驗,關(guān)于兩者的性能比較請參考PHP Manual中的相關(guān)章節(jié):http://www.php.net/manual/en/features.gc.performance-considerations.php。

首先是內(nèi)存泄露試驗,下面直接引用PHP Manual中的實驗代碼和試驗結(jié)果圖:

  1. <?php  
  2. class Foo  
  3. {  
  4.     public $var = '3.1415962654';  
  5. }  
  6.  
  7. $baseMemory = memory_get_usage();  
  8.  
  9. for ( $i = 0; $i <= 100000; $i++ )  
  10. {  
  11.     $a = new Foo;  
  12.     $a->self = $a;  
  13.     if ( $i % 500 === 0 )  
  14.     {  
  15.         echo sprintf( '%8d: '$i ), memory_get_usage() - $baseMemory"\n";  
  16.     }  
  17. }  
  18. ?> 

 

PHP

可以看到在可能引發(fā)累積性內(nèi)存泄露的場景下,PHP5.2發(fā)生持續(xù)累積性內(nèi)存泄露,而PHP5.3則總能將內(nèi)存泄露控制在一個閾值以下(與根緩沖區(qū)大小有關(guān))。

另外是關(guān)于性能方面的對比:

  1. <?php  
  2. class Foo  
  3. {  
  4.     public $var = '3.1415962654';  
  5. }  
  6.  
  7. for ( $i = 0; $i <= 1000000; $i++ )  
  8. {  
  9.     $a = new Foo;  
  10.     $a->self = $a;  
  11. }  
  12.  
  13. echo memory_get_peak_usage(), "\n";  
  14. ?> 

這個腳本執(zhí)行1000000次循環(huán),使得延遲時間足夠進行對比。

然后使用CLI方式分別在打開內(nèi)存回收和關(guān)閉內(nèi)存回收的的情況下運行此腳本:

  1. time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php  
  2. and 
  3. time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php 

在我的機器環(huán)境下,運行時間分別為6.4s和7.2s,可以看到PHP5.3的垃圾回收機制會慢一些,但是影響并不大。

與垃圾回收算法相關(guān)的PHP配置

可以通過修改php.ini中的zend.enable_gc來打開或關(guān)閉PHP的垃圾回收機制,也可以通過調(diào)用gc_enable()或gc_disable()打開或關(guān)閉PHP的垃圾回收機制。在PHP5.3中即使關(guān)閉了垃圾回收機制,PHP仍然會記錄可能根到根緩沖區(qū),只是當(dāng)根緩沖區(qū)滿額時,PHP不會自動運行垃圾回收,當(dāng)然,任何時候您都可以通過手工調(diào)用gc_collect_cycles()函數(shù)強制執(zhí)行內(nèi)存回收。

原文鏈接:http://www.cnblogs.com/leoo2sk/archive/2011/02/27/php-gc.html

【編輯推薦】

  1. 25個不錯PHP的游戲編程腳本代碼分享
  2. PHP企業(yè)級應(yīng)用之常見緩存技術(shù)深入解讀
  3. PHP與Java在Web開發(fā)方面的比較
  4. 40個跡象表明你還是PHP菜鳥
  5. PHP程序設(shè)計最佳實踐
責(zé)任編輯:陳貽新 來源: 張洋的博客
相關(guān)推薦

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2022-01-20 10:34:49

JVM垃圾回收算法

2010-12-13 11:14:04

Java垃圾回收算法

2017-03-03 09:26:48

PHP垃圾回收機制

2021-11-05 15:23:20

JVM回收算法

2020-07-09 08:26:42

Kubernetes容器開發(fā)

2009-12-25 16:15:31

JVM垃圾回收算法

2025-01-06 08:22:41

2017-10-12 12:41:11

PHP圾回收機制變量容器

2009-12-09 17:28:34

PHP垃圾回收機制

2017-06-12 17:38:32

Python垃圾回收引用

2012-01-09 16:53:36

JavaJVM

2021-01-04 10:08:07

垃圾回收Java虛擬機

2019-12-02 16:23:03

Python編程語言“垃圾”回收

2021-12-02 18:20:25

算法垃圾回收

2021-03-03 08:13:57

模式垃圾回收

2021-02-04 10:43:52

開發(fā)技能代碼

2010-09-26 13:29:46

JVM垃圾回收

2010-01-14 11:28:54

JVM分代垃圾回收
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 亚洲精品久久久蜜桃网站 | h视频免费看 | 国产美女自拍视频 | 日韩a视频 | 美女日皮网站 | 欧美日韩一区二区电影 | 天堂视频免费 | 日韩av美女电影 | 久热国产在线 | 一级毛片免费完整视频 | 精品在线观看一区 | 日韩精品成人一区二区三区视频 | 国产精品99久久久精品免费观看 | 久久久国产精品视频 | 日韩福利电影 | 成人在线观看免费视频 | 精品久久久久久亚洲精品 | 欧美在线视频一区二区 | 日韩不卡一二区 | 国产精品亚洲欧美日韩一区在线 | 国产一区二区三区在线 | 日韩欧美国产精品 | 中文精品一区二区 | 成人性视频免费网站 | 欧美日韩一区二区三区视频 | 国产精品久久久久久久免费大片 | 一区二区在线不卡 | 性国产xxxx乳高跟 | 超碰成人免费 | 欧美日韩综合一区 | 青青草华人在线视频 | 91精品国产综合久久久久久丝袜 | 欧美高清性xxxxhdvideosex | 男女视频免费 | 美女天天干天天操 | 福利电影在线 | 欧美一级三级在线观看 | 欧美国产亚洲一区二区 | 99精品国产一区二区三区 | 毛片.com | 中文字幕av亚洲精品一部二部 |