垃圾回收是一种自动的内存管理机制,当一个变量在程序中不再被使用时,应该予以释放,这种内存资源管理称为垃圾回收。其中一种垃圾回收方式是使用引用计数,通过对数据存储的物理空间多附加一个计数器空间,当其他数据与其相关是,计数器加一,反之,相关解除时计数器减一。定期检查各存储对象的计数器,计数器为零的话,则认为该对象已经被抛弃而应将其所占物理空间回收。

下面是GC的核心数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
struct _zend_refcounted {
uint32_t refcount;
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type,
zend_uchar flags,
uint16_t gc_info)
} v;
uint32_t type_info;
} u;
};

zend_refcounted_h的结构

zend_refcounted由32位bit的refcount和32bit的type_info组成

1. type 第一个字节记录当前元素的类型

  1. flags 第二个字节用来记录数据类型

  2. gc_info 后面的两个字节标记当前元素的颜色和垃圾回收池中的位置,其中高地址的两位用来标记颜色。

3.1 颜色有 黑色,白色,灰色,紫色

php性能提升

gc垃圾收集器

下面是gc_globals的结构

gc_root_buffer 是一个双向链表,同时记录引用计数的相关信息,zend_gc_globals维护者gc的整个信息,这里列出个字段的含义。

序号 字段 含义
1 gc_enabled 是否开启gc
2 gc_active 垃圾回收算法是否运行
3 gc_full 垃圾缓冲区是否满了,在debug模式下有用
4 buf 垃圾缓冲区,PHP7默认大小为10 000个节点位置,0位置保留
5 roots 指向缓冲区中最新加入的可能是垃圾的元素
6 unused 指向缓冲区中没有使用的位置,在没有启动垃圾回收算法前,指向空
7 first_unused 指向缓冲区中第一个未使用的位置,新的元素插入缓冲区后,指针会后移动一位
8 last_unused 指向缓冲区中最后一个位置
9 to_free 待释放的列表
10 next_to_free 下一个待释放的列表
11 gc_runs 记录gc算法的运行的次数,当缓冲区满了,才会运行gc算法
12 collected 记录gc算法回收的垃圾数

垃圾收集过程

  1. 要求数据类型是数组和对象
  2. 没有在缓冲区中存在过
  3. 没有被标记过
  4. 将其gc_info标记为紫色的,且记录其在缓冲区的位置

当缓冲区满时,再收集新的元素会出发垃圾回收算法。

垃圾流转图:

回收过程分为4部分

  1. 对roots环中每一个元素进行深度优先遍历,将每个元素中gc_info为紫色的标记元素为灰色,且引用计数-1

  2. 扫描roots换中gc_info为灰色的元素,如果发现引用计数仍旧大于0,说明这个元素在其他地方使用,那么将其颜色重新标记为黑色,如果发现其引用计数是0则将其标记为白色,该过程同样为深度优先遍历。

  3. 扫描roots换,将gc_info颜色为黑色的的元素从roots移除,然后对roots中颜色为白色的元素进行深度有限遍历,将其引用计数+1,然后将roots链表移动到待释放列表中(to_free)中等待回收