淺析 PHP 的「垃圾回收」機制

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 種的垃圾回收機制,大家肯定都有所瞭解,比如如何確定垃圾,有兩種算法,引用計數法和可達性分析算法。Java 中使用的是可達性分析算法,而 PHP 使用的引用計數算法。我們都知道引用計數算法較難處理循環引用的問題,PHP 這波奇怪的操作可太秀了,那 PHP 的垃圾回收原理是怎麼樣的?","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"一、PHP 中的引用計數","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.1 如何確定垃圾","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"原理:","attrs":{}},{"type":"text","text":" 給對象添加一個引用計數器,每當有一個地方引用它,計數器的值就加一。每當有一個引用失效,計數器的值就減一。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個變量 value 的 refcount 減一之後等於 0,此 value 可以被","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"釋放","attrs":{}}],"attrs":{}},{"type":"text","text":"掉,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"不屬於","attrs":{}}],"attrs":{}},{"type":"text","text":"垃圾。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"垃圾回收器不會處理","attrs":{}},{"type":"text","text":" 。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個變量 value 的 refcount 減一之後還是大於 0,此 value 被認爲不能被釋放掉,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可能","attrs":{}}],"attrs":{}},{"type":"text","text":"成爲一個垃圾。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"垃圾回收器將可能的垃圾收集起來,等達到一定數量後開始啓動","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾鑑定程序","attrs":{}}],"attrs":{}},{"type":"text","text":",把","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"真正","attrs":{}}],"attrs":{}},{"type":"text","text":"的垃圾釋放掉。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"缺點:","attrs":{}},{"type":"text","text":" 需要維護引用計數器,有一定的消耗。且較難處理循環引用的問題。後面也會講到如何解決這個問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面的例子說明引用計數的是如何變化的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6e/6ecd001be906c109e6d8a7c01a85d19f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.2 PHP 中的變量知識","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個 php 變量存在一個叫 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"zval","attrs":{}}],"attrs":{}},{"type":"text","text":" 的變量容器中。一個 zval 變量容器,除了包含變量的類型和值,還包括兩個字節的額外信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"is_ref","attrs":{}}],"attrs":{}},{"type":"text","text":",是個 bool 值,用來標識這個變量是否是屬於引用集合(reference set) 。通過這個字節,php 引擎才能把普通變量和引用變量區分開來,由於 php 允許用戶通過使用&來使用自定義引用,zval 變量容器中還有一個內部引用計數機制,來優化內存使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個額外字節是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"refcount","attrs":{}}],"attrs":{}},{"type":"text","text":",用以表示指向這個 zval 變量容器的變量(也稱符號即 symbol )個數。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.3 使用引用計數的類型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有 5 種類型用的引用計數:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"string、array、object、resource、reference","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面的表格說明了只有 type_flag 爲以下 8 種類型且 IS_TYPE_REFOUNTED=true 的變量才使用引用計數,如下表所示","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/df/df82c4fced28664329f69660f2a75b45.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"二、回收原理","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1. 回收時機","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自動回收:在變量 zval 斷開 value 的指向時,如果發現 refcount=0 則會直接釋放 value。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"斷開 value 指向的情形","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":2,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改變量時會斷開原有 value 的指向","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":2,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函數返回時會釋放所有的局部變量","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主動回收","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"unset()","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數。類似於 Java 中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.gc()","attrs":{}}],"attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.2 垃圾鑑定","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"垃圾收集器收集的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可能垃圾","attrs":{}}],"attrs":{}},{"type":"text","text":"到達一定數量後,啓動垃圾鑑定、回收程序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原理:垃圾是由於成員引用自身導致的,那麼就對 value 的 refcount 減一操作,如果 value 的 refount 變爲了 0,則表明其引用全部來自自身成員,value 屬於垃圾。另外垃圾只會出現在array、object類型中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/447c2d361d2bd1a09cb96a9828200383.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"步驟一:","attrs":{}},{"type":"text","text":" 遍歷垃圾回收器的 buffer 緩衝區,把 value 標爲灰色,把 value 的成員的 refount-1,還是標爲灰色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"步驟二:","attrs":{}},{"type":"text","text":" 遍歷垃圾回收器的 buffer 緩衝區,如果 value 的 refcount 等於 0,標爲白色,認爲是垃圾;如果不等於 0,則表示還有外部的引用,不是垃圾,將 refcount+1 還原回去,標爲黑色。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"步驟三:","attrs":{}},{"type":"text","text":" 遍歷垃圾回收器的 buffer 緩衝區,將 value 爲非白色的節點從 buffer 中刪除,最終 buffer 緩衝區中都是真正的垃圾。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"步驟四:","attrs":{}},{"type":"text","text":" 遍歷垃圾回收器的 buffer 緩衝區,釋放此 value。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"三、帶你看源碼","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 垃圾管家","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我稱 _zend_gc_globals 爲垃圾管家,結構體會對垃圾進行管理,收集到的可能成爲垃圾的 value 就保存在這個結構的 buf 中,稱爲垃圾緩存區。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件路徑:\\Zend\\zend_gc.h。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4d/4d2a8bb73e73c66ff5883b533311a101.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 垃圾管家初始化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)php.ini 解析後調用 gc_init() 初始垃圾管家_zend_gc_globals","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件路徑:\\Zend\\zend_gc.c","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/2227aa0b757d35d42b18644c2d5071ce.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)gc_init() 函數裏面調用 gc_reset() 函數初始化。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0e/0ec25c8bb86e96af9c871f24f8367b63.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 判斷是否需要收集","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)在銷燬一個變量時就會判斷是否需要收集。調用 i_zval_ptr_dtor() 函數","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​ 文件路徑:Zend\\zend_variables.h","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2d/2d27cd70fcef7a9c4372b1cffb0f7850.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 refcount 減一後,refcount 等於 0,則認爲不是垃圾,調用 _zval_dtor_func 方法釋放此 value。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 refcount 減一後,refcount 大於 0,則認爲 value 可能是垃圾,垃圾管家進行收集","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 收集垃圾","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件路徑:\\Zend\\zend_gc.c,調用方法:gc_possible_root","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/54cf29c5d05065c0b67dae942731587e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拿出 unused 指向的節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果拿出的節點是可用的,則將 unused 指向下一個節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 unused 沒有可用的,且 first_unused 還沒有推進到 last_unused,則表示 buf 緩存區中還有可用的節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拿出 first_unused 指向的節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"first_unused 指向下一個節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"buf 緩存區已滿,啓動垃圾鑑定、垃圾回收。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果未啓用垃圾回收,則直接返回。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/60/604f62085c732697fcd938341b59d160.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將插入的變量標爲紫色,防止重複插入。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將該節點在 buf 數組中的位置保存到了 gc_info 中,當後續 value 的 refcount 變爲了 0。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要將其從 buf 中刪除時可以知道該 value 保存在哪個 gc_root_buffer 中。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"5. 釋放垃圾","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於回收方法 zend_gc_collect_cycles() 實在是太長,我把幾個關鍵步驟理出來了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/00/008f107be893870bf1c73214eb72d7e2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"掃描根節點","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"收集根節點","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用回收器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"清理變量","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"收集完成","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"四、總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)PHP 的垃圾回收和 Java 的垃圾回收還是很有很大區別的,我們都以爲沒有高級語言會用到引用計數法來回收垃圾,但偏偏 PHP 用的是引用計數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)PHP 用了一套自己的算法來解決因循環引用而產生垃圾的問題,這套算法可以簡單理解爲先把可疑垃圾的引用計數減一來進行測試,如果引用計數確實等於 0 ,則標記顏色爲黑色,後續一起清理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)PHP 垃圾收集中總共用到了三種關鍵顏色: 白色- 垃圾, 黑色- 非垃圾, 紫色- 防止重複插入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎關注我的公衆號:「","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"悟空聊架構","attrs":{}}],"attrs":{}},{"type":"text","text":"」","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者簡介:8 年互聯網職場老兵|全棧工程師|90 後超級奶爸|開源踐行者|公衆號萬粉原創號主。藍橋簽約作者,著有《JVM 性能調優實戰》專欄,手寫了一套 7 萬字 SpringCloud 實戰總結和 3 萬字分佈式算法總結。歡迎關注我的公衆號「悟空聊架構」,免費獲取資料學習。","attrs":{}}]}],"attrs":{}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章