PHP的垃圾回收机制
Laiyong Wang Lv5

PHP 的垃圾回收机制基于引用计数和周期性垃圾回收两种方式。

  • 引用计数
    PHP 中的变量是以引用计数的方式来管理的。每当一个变量被赋予一个新的值或者被另一个变量引用时,它的引用计数就会增加;当变量不再被引用时,引用计数减少。当引用计数减少到零时,PHP 会自动释放该变量所占用的内存。
  • 周期性垃圾回收
    除了引用计数外,PHP 还会定期进行垃圾回收。周期性垃圾回收是为了解决引用计数无法处理的循环引用问题。例如,如果两个对象相互引用,它们的引用计数永远不会降为零,因此需要周期性垃圾回收来检测并释放这些循环引用所占用的内存。PHP 使用标记清除(Mark and Sweep)算法来处理循环引用和其他无法通过引用计数检测的情况。

解释

PHP的变量都存在一个叫 zval 的变量容器中,zval 变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。

  • refcount
    表示一个变量当前的引用计数。引用计数是 PHP 内存管理中的一部分,用于跟踪变量的引用情况。当一个变量被赋值给另一个变量时,其引用计数会增加;当变量不再被引用时,其引用计数会减少。

  • is_ref
    一个整数类型的标志位。当 is_ref 为 1 时,表示该 zval 是一个引用,否则不是。但是,is_ref 是 zval 结构体的一个属性,而不是 is_ref 函数所表示的含义。

到这里,可以发现引用计数已可删除垃圾变量,为啥还要周期性垃圾回收呢?

因为zval容器还有复合类型(例如数组、对象),正常情况下也适用引用计数,除特殊情况

1
2
$a = array( 'a' );
$a[] = &$a;

这样的话,引用计数为2,就算unset销毁 $a,引用计数还为1,尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),但引用计数不为0,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏,好消息时程序结束时会自动回收,若是程序未结束,且数量过多就会产生问题

周期性垃圾回收的使用标记清除(Mark and Sweep)算法(ChatGPT的答案)

标记清除(Mark and Sweep)算法是一种常见的内存回收算法,用于处理动态分配的内存中的垃圾。这个算法通常用于管理堆(heap)内存,其中动态分配的对象被存储。

算法主要包含两个阶段:标记阶段和清除阶段。

  • 标记阶段
    在这个阶段,算法从根对象开始遍历内存中的对象,并标记所有能够被访问到的对象。在典型的垃圾收集中,根对象通常是全局变量、调用栈中的局部变量以及静态变量等。算法会递归遍历对象引用链,标记所有可以被访问到的对象。这个阶段结束后,所有能够被访问到的对象都被标记为“活动”或“存活”,而未被标记的对象被视为“垃圾”。

  • 清除阶段
    在标记阶段结束后,所有未被标记的对象都被认为是垃圾,需要被清除。在清除阶段,算法会遍历整个堆内存,释放所有未被标记的对象所占用的内存空间。这个阶段的主要目的是回收未被引用的对象所占用的内存,以便重新利用。

标记清除算法的优点是简单且高效,它可以处理循环引用等复杂情况,并且不需要暂停应用程序的执行。然而,它也有一些缺点,比如可能会产生内存碎片,导致内存分配效率降低。

总的来说,标记清除算法是一种常见且有效的垃圾回收算法,被广泛应用于许多动态语言的运行时环境中,如Python、Ruby和JavaScript等。

 Comments