淺談PHP源碼三十四:PHP5.3新增加的垃圾回收機制(Garbage Collection)
在之前的文章淺談PHP源碼三十三:PHP5.3新增加的垃圾回收機制(Garbage Collection)基礎 中有介紹了垃圾回收機制的一些基礎知識。今天我們看看其初始化,添加到垃圾緩沖區和垃圾回收的過程。
官方說明文檔請猛擊Garbage Collection
中文版地址:http://docs.php.net/manual/zh/features.gc.php
【初始化】
在zend/zend_gc.c 121行有函數gc_init實現了gc的初始化,其代碼如下:
ZEND_API void gc_init(TSRMLS_D){if (GC_G(buf) == NULL GC_G(gc_enabled)) {GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);GC_G(last_unused) = GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];gc_reset(TSRMLS_C);}}
第123行判斷是否為空和是否開啟了gc,如果都為真,則轉124行
第124行是直接調用malloc分配了10000個gc_root_buffer內存。
第125行將html' target='_blank'>全局變量last_unused設置為gc緩沖區的結束位置。
第126行重置整個垃圾收集機制,其代碼從zend/zend_gc.c 88行開始,如下:
ZEND_API void gc_reset(TSRMLS_D){GC_G(gc_runs) = 0;GC_G(collected) = 0; #if GC_BENCHGC_G(root_buf_length) = 0;GC_G(root_buf_peak) = 0;GC_G(zval_possible_root) = 0;GC_G(zobj_possible_root) = 0;GC_G(zval_buffered) = 0;GC_G(zobj_buffered) = 0;GC_G(zval_remove_from_buffer) = 0;GC_G(zobj_remove_from_buffer) = 0;GC_G(zval_marked_grey) = 0;GC_G(zobj_marked_grey) = 0;#endif GC_G(roots).next = GC_G(roots);GC_G(roots).prev = GC_G(roots); if (GC_G(buf)) {GC_G(unused) = NULL;GC_G(first_unused) = GC_G(buf); GC_G(zval_to_free) = NULL;} else {GC_G(unused) = NULL;GC_G(first_unused) = NULL;GC_G(last_unused) = NULL;}}
第90~91行 設置gc運行的次數統計(gc_runs)和gc中垃圾的個數(collected)為0。
第106~107行 設置雙向鏈表頭結點的上一個結點和下一個結點指向自己。
關于gc_enabled,默認情況下是開啟的,可以在php.ini中配置。
其實現代碼在zend/zend.c 93行 如下:
STD_ZEND_INI_BOOLEAN( zend.enable_gc , 1 ,ZEND_INI_ALL,OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
初始化調用在zend/zend.c 79 行
static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */{OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); if (GC_G(gc_enabled)) {gc_init(TSRMLS_C);} return SUCCESS;}
【添加到垃圾緩沖區】
跟蹤PHP的源碼 zend/zend_execute_API.c 424行
[_zval_ptr_dtor] - [GC_ZVAL_CHECK_POSSIBLE_ROOT()] - [gc_zval_check_possible_root()] - [gc_zval_possible_root()]
其中在gc_zval_check_possible_root()函數中,僅對數組和對象執行垃圾回收操作
gc_zval_possible_root函數的代碼如下:
ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC){if (UNEXPECTED(GC_G(free_list) != NULL GC_ZVAL_ADDRESS(zv) != NULL GC_ZVAL_GET_COLOR(zv) == GC_BLACK) (GC_ZVAL_ADDRESS(zv) GC_G(buf) || GC_ZVAL_ADDRESS(zv) = GC_G(last_unused))) {/* The given zval is a garbage that is going to be deleted by * currently running GC */return;} if (zv- type == IS_OBJECT) {GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);return;GC_BENCH_INC(zval_possible_root); if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {GC_ZVAL_SET_PURPLE(zv); if (!GC_ZVAL_ADDRESS(zv)) {gc_root_buffer *newRoot = GC_G(unused); if (newRoot) {GC_G(unused) = newRoot- prev;} else if (GC_G(first_unused) != GC_G(last_unused)) {newRoot = GC_G(first_unused);GC_G(first_unused)++;} else {if (!GC_G(gc_enabled)) {GC_ZVAL_SET_BLACK(zv);return;zv- refcount__gc++;gc_collect_cycles(TSRMLS_C);zv- refcount__gc--;newRoot = GC_G(unused);if (!newRoot) {return;GC_ZVAL_SET_PURPLE(zv);GC_G(unused) = newRoot- prev;newRoot- next = GC_G(roots).next;newRoot- prev = GC_G(roots);GC_G(roots).next- prev = newRoot;GC_G(roots).next = newRoot; GC_ZVAL_SET_ADDRESS(zv, newRoot); newRoot- handle = 0;newRoot- u.pz = zv; GC_BENCH_INC(zval_buffered);GC_BENCH_INC(root_buf_length);GC_BENCH_PEAK(root_buf_peak, root_buf_length);}}
第132~140行 檢查zval結點信息是否已經放入到結點緩沖區,如果已經放入到結點緩沖區,則直接返回,這樣可以優化其性能
第142~145行 處理對象結點,直接返回,不再執行后面的操作
第149行 判斷結點是否已經被標記為紫色,如果為紫色則不再添加到結點緩沖區,此處在于保證一個結點只執行一次添加到緩沖區的操作。
第150行 將結點的顏色標記為紫色,表示此結點已經添加到緩沖區,下次不用再做添加
第153~157行 找出新的結點的位置,如果緩沖區滿了,則執行垃圾回收操作。
第176~184行 將新的結點添加到緩沖區所在的雙向鏈表。
【垃圾回收過程】
在gc_zval_possible_root函數中,當緩沖區滿時,程序調用gc_collect_cycles函數,執行垃圾回收操作。從zend/zend_gc.c文件615行開始,其實現代碼如下:
ZEND_API int gc_collect_cycles(TSRMLS_D){int count = 0; if (GC_G(roots).next != GC_G(roots)) {zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free; if (GC_G(gc_active)) {return 0;GC_G(gc_runs)++;GC_G(zval_to_free) = FREE_LIST_END;GC_G(gc_active) = 1;gc_mark_roots(TSRMLS_C);gc_scan_roots(TSRMLS_C);gc_collect_roots(TSRMLS_C); orig_free_list = GC_G(free_list);orig_next_to_free = GC_G(next_to_free);p = GC_G(free_list) = GC_G(zval_to_free);GC_G(zval_to_free) = NULL;GC_G(gc_active) = 0; /* First call destructors */while (p != FREE_LIST_END) {if (Z_TYPE(p- z) == IS_OBJECT) {if (EG(objects_store).object_buckets EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].valid EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.refcount = 0 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.dtor !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].destructor_called) { EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].destructor_called = 1;EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.refcount++;EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.object, Z_OBJ_HANDLE(p- z) TSRMLS_CC);EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.refcount--;count++;p = p- u.next;} /* Destroy zvals */p = GC_G(free_list);while (p != FREE_LIST_END) {GC_G(next_to_free) = p- u.next;if (Z_TYPE(p- z) == IS_OBJECT) {if (EG(objects_store).object_buckets EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].valid EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.refcount = 0) {EG(objects_store).object_buckets[Z_OBJ_HANDLE(p- z)].bucket.obj.refcount = 1;Z_TYPE(p- z) = IS_NULL;zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p- z), Z_OBJ_HT(p- z) TSRMLS_CC);} else if (Z_TYPE(p- z) == IS_ARRAY) {Z_TYPE(p- z) = IS_NULL;zend_hash_destroy(Z_ARRVAL(p- FREE_HASHTABLE(Z_ARRVAL(p- } else {zval_dtor( p- Z_TYPE(p- z) = IS_NULL;p = GC_G(next_to_free);} /* Free zvals */p = GC_G(free_list);while (p != FREE_LIST_END) {q = p- u.next;FREE_ZVAL_EX( p- p = q;GC_G(collected) += count;GC_G(free_list) = orig_free_list;GC_G(next_to_free) = orig_next_to_free;} return count;}
第619行 判斷緩沖區是否為空,如果為空則不會執行垃圾回收操作
第622行 判斷垃圾回收操作是否正則進行,如果正在進行,則直接返回
第625~627行 將垃圾回收操作次數加1,初始化空閑列表,設置gc_active為1表示垃圾回歸正在進行
第628行 此處為其官方文檔中算法的步驟 B ,算法使用深度優先搜索查找所有可能的根,找到后將每個變量容器中的引用計數減1″,為確保不會對同一個變量容器減兩次”1″,用灰色標記已減過1的。
第629行 這是算法的步驟 C ,算法再一次對每個根節點使用深度優先搜索,檢查每個變量容器的引用計數。如果引用計數是 0 ,變量容器用白色來標記。如果引用次數大于0,則恢復在這個點上使用深度優先搜索而將引用計數減1的操作(即引用計數加1),然后將它們重新用黑色標記。
第630行 算法的最后一步 D ,算法遍歷根緩沖區以從那里刪除變量容器根(zval roots),同時,檢查是否有在上一步中被白色標記的變量容器。每個被白色標記的變量容器都被清除。
在[gc_collect_cycles() - gc_collect_roots() - zval_collect_white() ]中我們可以看到,對于白色標記的結點會被添加到全局變量zval_to_free列表中。此列表在后面的操作中有用到。
第632~633行 將全局變量free_list和next_to_free存放在相對應當的臨時變量中,在最后會恢復到此時的狀態。
第634~635行 初始化需要清除的列表,清空將要清空的zval列表并且將垃圾收集的操作狀態為不激活狀態。
第639~655行 第一次調用析構函數,并統計清除的變量個數
第657~678行 清除變量
第682~686行 釋放內存
第687~689行 處理垃圾個數統計,恢復free_list和next_to_free變量
以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP !
相關推薦:
淺談PHP源碼三十三:PHP5.3新增加的垃圾回收機制(Garbage Collection)基礎
淺談PHP源碼三十二:PHP內存池中的emalloc/efree層與堆(heap)層
淺談PHP源碼二十九:關于接口的繼承
以上就是淺談PHP源碼三十四:PHP5.3新增加的垃圾回收機制(Garbage Collection)的詳細內容,PHP教程
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答