成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【轉(zhuǎn)】淺談PHP5中垃圾回收算法(Garbage Collection)的演化

AdolphLWQ / 1420人閱讀

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

【轉(zhuǎn)】淺談PHP5中垃圾回收算法(Garbage Collection)的演化
前言

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

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

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

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

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

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;
 
struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount__gc;
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};

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

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

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

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

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

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

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

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

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

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

由于Reference Counting的這個(gè)缺陷,PHP5.3改進(jìn)了垃圾回收算法。

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

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

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

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

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

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

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

1、對每個(gè)根緩沖區(qū)中的根zval按照深度優(yōu)先遍歷算法遍歷所有能遍歷到的zval,并將每個(gè)zval的refcount減1,同時(shí)為了避免對同一zval多次減1(因?yàn)榭赡懿煌母鼙闅v到同一個(gè)zval),每次對某個(gè)zval減1后就對其標(biāo)記為“已減”。

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

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

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

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

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

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

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

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

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

self = $a;
    if ( $i % 500 === 0 )
    {
        echo sprintf( "%8d: ", $i ), memory_get_usage() - $baseMemory, "
";
    }
}
?>

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

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

self = $a;
}
 
echo memory_get_peak_usage(), "
";
?>

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

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

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

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

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

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

轉(zhuǎn)自:http://www.cnblogs.com/leoo2s...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/22424.html

相關(guān)文章

  • 譯文-垃圾回收器是什么

    摘要:垃圾回收器追蹤所有正在使用的對象,將無用對象標(biāo)記為垃圾。自動(dòng)化指針內(nèi)存回收自動(dòng)化的最好方式之一是使用鉤子函數(shù)。它們可能因?yàn)槎喾N原因發(fā)生,但是這種垃圾回收器是最主流的一種。 原文出處:What Is Garbage Collection? 一眼就應(yīng)該從名稱看出垃圾回收機(jī)制的含義-查找垃圾,然后丟棄。事實(shí)正好相反。垃圾回收器追蹤所有正在使用的對象,將無用對象標(biāo)記為垃圾。請留意,我們開始研究...

    alanoddsoff 評論0 收藏0
  • 細(xì)述 Java垃圾回收機(jī)制→How Java Garbage Collection Works?

    摘要:當(dāng)一個(gè)實(shí)例被創(chuàng)建的時(shí)候,它最初被存放在堆內(nèi)存空間的年輕代的區(qū)中。老年代或者永久代是堆內(nèi)存的第二個(gè)邏輯部分。在垃圾回收過程中掃描屬于部分的堆內(nèi)存。一旦實(shí)例從堆內(nèi)存中刪除了,它們原來的位置將空出來給以后分配實(shí)例使用。 本文非原創(chuàng),翻譯自How Java Garbage Collection Works?在Java中為對象分配和釋放內(nèi)存空間都是由垃圾回收線程自動(dòng)執(zhí)行完成的。和C語言不一樣的是...

    cc17 評論0 收藏0
  • Node - 內(nèi)存管理和垃圾回收

    摘要:的內(nèi)存限制和垃圾回收機(jī)制內(nèi)存限制內(nèi)存限制一般的后端語言開發(fā)中,在基本的內(nèi)存使用是沒有限制的。的內(nèi)存分代目前沒有一種垃圾自動(dòng)回收算法適用于所有場景,所以的內(nèi)部采用的其實(shí)是兩種垃圾回收算法。 前言 從前端思維轉(zhuǎn)變到后端, 有一個(gè)很重要的點(diǎn)就是內(nèi)存管理。以前寫前端因?yàn)橹皇窃跒g覽器上運(yùn)行, 所以對于內(nèi)存管理一般不怎么需要上心, 但是在服務(wù)器端, 則需要斤斤計(jì)較內(nèi)存。 V8的內(nèi)存限制和垃圾回收機(jī)...

    joyqi 評論0 收藏0
  • JavaScript深入淺出第3課:什么是垃圾回收算法?

    摘要:摘要是如何回收內(nèi)存的深入淺出系列深入淺出第課箭頭函數(shù)中的究竟是什么鬼深入淺出第課函數(shù)是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個(gè)話題非常火,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對垃圾進(jìn)行回收再利用。 摘要: JS是如何回收內(nèi)存的? 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼? Jav...

    AaronYuan 評論0 收藏0
  • Java 垃圾回收(GC) 泛讀

    摘要:在這種消耗很高的狀態(tài)下,應(yīng)用程序所有的線程都會(huì)掛起,暫停一切正常的工作,等待垃圾回收的完成。但是,因?yàn)榫€程切換和上下文轉(zhuǎn)換的消耗,會(huì)使得垃圾回收的總體成本上升,造成系統(tǒng)吞吐量的下降。 Java 垃圾回收(GC) 泛讀 文章地址: https://segmentfault.com/a/1190000008922319 0. 序言 帶著問題去看待 垃圾回收(GC) 會(huì)比較好,一般來說主要的...

    haoguo 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<