這篇文章主要介紹了關於淺談PHP源碼三十四:PHP5.3新增加的垃圾回收機制(Garbage Collection),有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
淺談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行將全域變數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_BENCH GC_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()] -root&Fs_cal;函數中,僅對數組和物件執行垃圾回收操作
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->z)); FREE_HASHTABLE(Z_ARRVAL(p->z)); } else { zval_dtor(&p->z); 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->z); 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源碼三十三:PHP5.3新增加的垃圾回收機制(Garbage Collection)基礎
淺談PHP源碼三十二:PHP記憶體池中的emalloc/efree層與堆(heap)層
以上是淺談PHP原始碼三十四:PHP5.3新增加的垃圾回收機制(Garbage Collection)的詳細內容。更多資訊請關注PHP中文網其他相關文章!