zoukankan      html  css  js  c++  java
  • php垃圾回收机制

    php变量存储在叫zval的容器中,下文用变量容器说明,这个容器包含了变量类型,变量值,是否是引用变量is_ref ,容器引用次数refcount 四个部分。

    引用计数机制

    标准变量

    将一个常量赋值给一个变量时就会创建一个变量容器,如下

    <?php
    $a = 'a string';
    ?>
    

    如果你安装了Xdebug扩展,可以使用xdebug_debug_zval()查看效果

    <?php
    $a = 'a string';
    xdebug_debug_zval('a');
    ?>
    

    结果为

    a: (refcount=1, is_ref=0)='a string'
    

    如果把变量$a赋值给另一个变量$b,不会新创建新变量容器,但是引用次数会加1,如下

    <?php
    $a = 'a string';
    $b = $a;
    xdebug_debug_zval('a');
    ?>
    

    结果为

    a: (refcount=2, is_ref=0)='a string'
    

    当变量离开作用域(比如所在函数执行完),或者使用unset()方法时,对应的变量容器的refcount会减1,当refcount为0时,变量容器销毁,内存回收。

    复合变量

    <?php
    $a = array('meaning' => 'life', 'number' => 42);
    xdebug_debug_zval('a');
    ?>
    

    上面的将产生3个变量容器,ameaningnumberrefcount的加减规则和标准变量一样。

    <?php
    $a = array('meaning' => 'life', 'number' => 42);
    $a['life'] = $a['meaning'];
    xdebug_debug_zval('a');
    ?>
    

    结果

    a: (refcount=1, is_ref=0)=array (
       'meaning' => (refcount=2, is_ref=0)='life',
       'number' => (refcount=1, is_ref=0)=42,
       'life' => (refcount=2, is_ref=0)='life'
    )
    

    上面的过程并不会创建新的变量容器,meaninglife指向同一个变量容器,所以refcount为2,看下面的图更容易理解。

    循环引用问题

    <?php
    $a = array('one');
    $a[] = &$a;
    xdebug_debug_zval('a');
    ?>
    

    结果

    a: (refcount=2, is_ref=1)=array (
       0 => (refcount=1, is_ref=0)='one',
       1 => (refcount=2, is_ref=1)=...
    )
    

    上面的过程会创建2个变量容器,其中a和数组a索引为1的变量指向同一个变量容器,所以refcount为2,如下图

    如果对数组a执行unset操作,变量容器会如下图所示

    由于变量容器的refcount仍为1,不能为清除,同时由于没有变量在指向这个变量容器,没有办法对refcount进行减1操作,这个变量容器会一直存在直到脚本结束,如果有大量这种变量容器存在,很可能会造成内存泄漏。为了解决这个问题,php5.3以后引入了新的垃圾回收机制,GC(Garbage Collector)。

    GC机制

    关于垃圾回收,主要有3条基本原则

    • refcount在增加,不是垃圾
    • refcount减小为0,变量容器将被清除
    • refcount减小但是大于0,变量容器仍存在,此时将使用GC机制
      GC机制的整个过程如下图

    下面对上面的ABCD步骤进行逐一解释
    A: 为了避免每当refcount减少的时候都调用GC机制,算法会把可能的变量容器(zval)都放入根缓冲区(root buffer),用紫色标记,这些变量容器是疑似垃圾。仅仅当缓冲区满的时候,才会对这些变量容器进行垃圾回收操作。
    B: 对根缓冲区内所有变量容器的refcount进行减1操作,为了避免对同一变量容器重复操作,减1后标记为灰色。
    C: 检查根缓冲区内所有变量容器,refcount为0的标记为白色(垃圾),refcount大于0的,将refcount进行加1操作(对B操作的还原),标记为黑色。
    D: 释放标记为白色的变量容器。

    整个过程可以概括为疑似垃圾放入根缓冲区,减操作,恢复操作,清除垃圾。
    注意,当$a = null时,a对应的变量容器的refcount为0。

    参考

    垃圾回收机制
    php底层原理之垃圾回收机制
    PHP的GC垃圾收集机制

  • 相关阅读:
    Git 在Idea下的操作
    负载均衡算法-java实现
    MySQL 上亿大表优化实践 转
    盘点 10 个代码重构的小技巧
    wireshark抓包工具详细说明及操作使用
    限流
    Semaphore
    CyclicBarrier
    CountDownLatch和枚举配合使用
    ReentrantReadWriteLock读写锁
  • 原文地址:https://www.cnblogs.com/whyly/p/13279465.html
Copyright © 2011-2022 走看看