各类知识收集,PHP技术分享与解决方案各类知识收集,PHP技术分享与解决方案各类知识收集,PHP技术分享与解决方案

Str Tom,为分享PHP技术和解决方案,贡献一份自己的力量!
收藏本站(不迷路),每天更新好文章!
当前位置:首页 > CMS教程 > PHP

浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(GarbageCollection)

管理员 2023-09-05
PHP
202

浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(GarbageCollection)

内容导读

收集整理的这篇技术教程文章主要介绍了浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(GarbageCollection),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8461字,纯文字阅读大概需要13分钟

内容图文

这篇文章主要介绍了关于浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(Garbage Collection),有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(Garbage Collection)
在之前的文章浅谈PHP源码三十三:PHP5.3新增加的垃圾回收机制(Garbage Collection)基础 中有介绍了垃圾回收机制的一些基础知识。今天我们看看其初始化,添加到垃圾缓冲区和垃圾回收的过程。
官方说明文档请猛击Garbage Collection
中文版地址:http://docs.php.com/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_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->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中文网!

相关推荐:

浅谈PHP源码三十三:PHP5.3新增加的垃圾回收机制(Garbage Collection)基础

浅谈PHP源码三十二:PHP内存池中的emalloc/efree层与堆(heap)层

浅谈PHP源码二十九:关于接口的继承

以上就是浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(Garbage Collection)的详细内容,更多请关注Gxl网其它相关文章!

内容总结

以上是为您收集整理的浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(GarbageCollection)全部内容,希望文章能够帮你解决浅谈PHP源码三十四:PHP5.3新增加的垃圾回收机制(GarbageCollection)所遇到的程序开发问题。 如果觉得技术教程内容还不错,欢迎将网站推荐给程序员好友。

内容备注

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

扫码关注

qrcode

QQ交谈

回顶部