• PHP垃圾回收机制


      PHP引用计数内存机制,无法处理循环引用内存泄漏,故通过回收周期(Collecting Cycles)来清理循环引用变量达到节省内存占用和防止内存泄露的目的。回收周期只是PHP所涉及的垃圾回收处理方式的一部分,下文也主要讲述回收周期的相关概念。

    1. 引用计数php变量存在一个叫"zval"的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息:is_ref和refcount。is_ref是个bool值,用来标识这个变量是否是属于引用集合,php引擎才能把普通变量和引用变量区分开来;refcount用以表示指向这个zval变量容器的变量个数,当refcount = 0意味着该变量可被清除或回收了
      <?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'
      )

       Xdebug的输出显示为两个值为'life'的 zval 变量容器,其实是同一个:

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

      上例结果如下:

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

      上例尽管不再有某个作用域中的任何符号指向这个变量容器,由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。php将在脚本执行结束时清除这个数据结构,但是在php清除之前,将耗费不少内存。如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这样的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更有可能出现这种情况,因为对象总是隐式的被引用。

    2. 可能根(Possible Root):即可能的垃圾根(possible garbage root,zval变量容器),如上例中最后 $a 所对应的变量容器虽然已没有被任何变量引用却不能被回收而成为了可能根。
    3. 根缓冲区(Root Buffer):为避免不得不检查所有引用计数可能减少的垃圾周期,而把所有可能根放在根缓冲区中,同时确保每个可能根在缓冲区中只出现一次。仅仅在根缓冲区满时,才对缓冲区内所有可能根执行垃圾回收操作。当回收周期机制关闭时,如果根缓冲区存满了可能根,更多的可能根显然不会被记录,并不会被回收周期算法来分析处理,就算他们是循环引用变量,也不能被清除进而导致内存泄漏。
    4. 回收周期:一个变量容器,如果其引用计数增加,将继续被使用,当然就不应被视为可能根;但如果引用计数减少到零,其将被清除(free)。换句话说,仅仅在引用计数减少到非零值时,才有可能被记为可能根,并产生垃圾周期(garbage cycle)。在一次垃圾回收周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。回收周期算法采用模拟删除、模拟恢复和真的删除。
    5. 打开和关闭回收周期机制:除了修改配置zend.enable_gc ,也能通过分别调用gc_enable() 和 gc_disable()函数来打开和关闭回收周期机制。另外,即使在回收周期机制不可用时,可能根也被记录,这样每次找到可能根后不检查回收周期机制是否打开,记录操作更快。gc_collect_cycles()函数可强制执行周期回收,并返回回收的周期数。
    6. 垃圾回收:垃圾回收在什么情况下发生呢?
      1. unset()或赋值NULL或其他导致refcount = 0的操作:unset()删除变量可能导致相应的变量容器refcount = 0,而NULL赋值直接refcount = 0;
      
      2. 函数执行结束:函数执行结束后其局部变量将被回收;
      
      3. 调用gc_collect_cycles():强制执行周期回收;
      
      4. 可能根缓冲区满:将对其中所有变量容器执行周期回收;
      
      5. 脚本执行结束:脚本执行结束便会清除在当前脚本中所声明的所有变量。
  • 相关阅读:
    nopCommerce中缓存学习
    EF
    路由
    webapi的加密方式
    生成N位数字随机数
    C# DataTable 转 实体类
    WebBrowser 打印
    文件上传控件,格式统一
    sqlserver 表循环-游标、表变量、临时表
    VB 老旧版本维护系列---迷之集合- dataTable
  • 原文地址:https://www.cnblogs.com/XiongMaoMengNan/p/7088203.html
Copyright © 2020-2023  润新知