是的,平时经常听到大牛说到的gc,就是垃圾回收器,全称Garbage Collection。
早期版本,准确地说是5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器的。只是简单的判断了一下变量的zval的refcount是否为0,是的话就释放否则不释放直至进程结束。
乍一看确实没毛病啊,然而其中隐藏着变量内存溢出的风险:http://bugs.php.net/bug.php?id=33595 ,无法回收的内存造成了内存泄漏,所以PHP5.3出现了专门负责清理垃圾数据、防止内存泄漏的GC。
PHP7中复杂类型的引用计数都维护在各个结构体头部的gc中
gc的作用是什么? 答 对垃圾回收的支持
什么是垃圾回收机制
垃圾回收是一种自动的内存管理机制,当一个变量在程序中不在被需要时(没有任何变量指向这个对象时),应给予以释放,这种内存资源管理称为垃圾回收,这是PHP的 GC 垃圾回收机制,目的是防止内存溢出;
其中一种垃圾回收的方式是使用引用计数,通过对数据存储的物理空间多附加一个计数器空间,当其他数据与相关时,计数器加一,反之相关解除时计数器减一。定期检查各存储对象的计数器,计数器为零的话,则认为对象已经被抛弃而应将其所占物理间回收
引用计数的基本知识
我们要了解GC,那么首先要了解引起垃圾回收的基数是什么。
在php中,每个变量存在一个叫“zval”的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括另外两个字节的额外信息。第一个是"is_ref"。第二个是"refcount"。
is_ref是一个布尔类型的值,用来标示这个变量是否属于引用集合。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过"&"来使用自定义的引用,所以zval中还有一个内部引用计数机制,来进行优化内存。
refcount用来表示这个zval变量容器的变量的个数。所有符号存在一个符号表当中,每个符号都有作用域。
通俗的讲:
- refcount就是多少个变量是一样的用了相同的值,那么refcount就是这个值
- is_ref就是当有变量用了&的形式进行赋值,那么is_ref的值就会增加
循环引用问题
在zend_reference结构体的引用计数减1,但仍然大于0,这时候,后面的结可能会成为垃圾,对此不处理可能会造成内存泄露,垃圾收集器会将这部分可能是垃圾的数据收集到缓冲区,同时加入到root环
gc_enabled 是否开启gc
gc_active 垃圾回收算法是否运行
gc_full 垃圾缓冲区是否满了,在debug模式下有用
buf 垃圾缓冲区,php7默认大小为10000个节点位置,第0个位置保留,既不会使用
roots: 指向缓冲区中最新加入的可能是垃圾的元素
unused 指向缓冲区中没有使用的位置,在没有启动垃圾回收算法前,指向空
first_unused 指向缓冲区第一个为未使用的位置。新的元素插入缓冲区后,指向会向后移动一位
last_unused 指向缓冲区最后一个位置
to_free 带释放的列表
next_to_free 下一个待释放的列表
gc_runs 记录gc算法运行的次数,当缓冲区满了,才会运行gc算法
collected 记录gc算法回收的垃圾数
垃圾收集器
- 要求数据类型的数组和对象
- 没有在缓冲区中存在过
- 没有被标记过
- 将其gc_info标记为紫色,且记录其在缓冲区的位置
当缓冲区满了,在收集到新的元素就会触发垃圾回收算法。
引用计数大于0说明它还在其他地方使用,那么先将元素的引用计数减1,如果发现引用计数为0,则说明任何地方都不再使用它,那么它就是垃圾,需要被回收掉,反之说明不是垃圾,需要将其从回收池移出去
垃圾回收算法
- 对roots环中每个元素进行深度优先遍历,将每个元素中gc_info位紫色的标记元素为灰色,且引用计数减1
- 扫描roots环中gc_info为灰色元素,如果发现其引用计数仍旧大于0,说明这个元素还在其他地方使用,那么将其颜色重新标记会黑色,并将其引用计数加1(第一步有减1操作)。如果发现其计数为0,则将其标记为白色,该过程同样为深度优先遍历
- 扫描roots环,将gc_info颜色为黑色的元素从roots移除,然后对roots中颜色为白色的元素进行深度优先遍历,将其引用计数加1(在第一步有减1操作),然后将roots链表移动到待释放的列表中(to_free)
- 释放to_free来列表的元素
php5.3的
引用计数系统中的同步周期回收,(Concurrent Cycle Collection in Reference Counted Systems) 算法来清除,基本规则是这样的,如果引用计数在+1.说明变量再用,不是垃圾。如果引用技术本身为0,那么是垃圾,需要删除。如果引用计数-1其为非0的时候,将其放到某一区块标记为疑似垃圾,然后队其做很多的模拟操作,最后找到真正的垃圾
通过php.ini 设置修改它的zend.enable_gc
或者通过gc_enable()和gc_disable()
早期版本,准确地说是5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器的。只是简单的判断了一下变量的zval的refcount是否为0,是的话就释放否则不释放直至进程结束。
php中垃圾是如何定义的?
准确地说,判断是否为垃圾,主要看有没有变量名指向变量容器zval,如果没有则认为是垃圾,需要释放。
5.3版本以后php是如何处理垃圾内存的?
判断处理过程
为解决环形引用导致的垃圾,产生了新的GC算法,遵守以下几个基本准则:
1.如果一个zval的refcount增加,那么此zval还在使用,不属于垃圾
2.如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾
3.如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾
are you ok?
来个白话文版:就是对此zval中的每个元素进行一次refcount减1操作,操作完成之后,如果zval的refcount=0,那么这个zval就是一个垃圾