美文网首页
PHP垃圾回收机制学习记录

PHP垃圾回收机制学习记录

作者: 拿破仑蛋糕 | 来源:发表于2017-08-31 23:21 被阅读0次

    参考文章:垃圾回收

    说在最前面:垃圾回收就是 处理无用的变量及其所关联内存的一种操作

    1. 基本思想

    想说清楚PHP的垃圾回收机制,需要从变量的实现说起(详细说明请查看)。变量的值存储到以下所示zval结构体中:

    typedef struct _zval_struct zval;
    ...
    struct _zval_struct {
        zvalue_value value;     /* 变量值 */
        zend_uchar type;    /* 变量类型 */
        zend_uint refcount__gc;     /* 引用计数,默认值为1 */
        zend_uchar is_ref__gc;    /* 是否被引用,默认值为0 */
    };
    

    当一个变量$a被赋值时就会生成一个zval变量容器,此时refcount__gc=1,当$a再赋值给其他的变量$b时,此时不再产生新的zval变量容器,而是将$b指向$a的zval变量容器,如下所示:

    <?php
    $a = "new string";
    xdebug_debug_zval( 'a' );
    //输出:a: (refcount=1, is_ref=0)='new string'
    $b = $a;
    xdebug_debug_zval( 'a' );
    //输出:a: (refcount=2, is_ref=0)='new string'
    

    当变量离开它的作用域(比如:函数执行结束),或者对变量调用了函数 unset()时,”refcount“就会减1,当”refcount“减为0时,则变量容器被销毁,内存被回收。

    2. 新老垃圾回收方式对比

    PHP5.2中的垃圾回收算法——Reference Counting :

    PHP5.2中使用的内存回收算法是大名鼎鼎的Reference Counting,中文翻译叫做“引用计数”,其思想非常直观和简洁:为每个内存对象分配一个计数器,当一个内存对象建立时计数器初始化为1(因此此时总是有一个变量引用此对象),以后每有一个新变量引用此内存对象,则计数器加1,而每当减少一个引用此内存对象的变量则计数器减1,当垃圾回收机制运作的时候,将所有计数器为0的内存对象销毁并回收其占用的内存。

    缺点: 这种方式虽然简单直观,实现方便,但却存在一个致命的缺陷,就是容易造成内存泄露,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露;。

    为解决环形引用导致的垃圾,产生了新的垃圾回收算法,遵守以下几个基本准则:
    1.如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾
    2.如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾

    PHP5.3中的新垃圾回收算法——Concurrent Cycle Collection in Reference Counted Systems :

    PHP会分配一个固定大小的“根缓冲区”,这个缓冲区可存放10000个的zval容器,当根缓冲区满或内存不足时,PHP就会执行垃圾回收。为了避免每次变量的refcount减少的时候都调用算法进行垃圾判断,算法会先把准则3情况下的zval节点放入节点缓冲区,并标记成紫色,确保每个节点在缓冲区中只出现一次。
    算法会分以下三步进行:MarkRoots()、ScanRoots()、CollectRoots()

    1. 从buffer链表的roots开始遍历,把当前value标为灰色,然后对当前value的成员进行深度优先遍历,把成员value的refcount减1,并且也标为灰色;
    2. 重复遍历buffer链表,检查当前value引用是否为0,为0则表示确实是垃圾,把它标为白色,如果不为0则排除了引用全部来自自身成员的可能,表示还有外部的引用,并不是垃圾,这时候因为步骤(1)对成员进行了refcount减1操作,需要再还原回去,对所有成员进行深度遍历,把成员refcount加1,同时标为黑色;
    3. 再次遍历buffer链表,将非白色的节点从roots链表中删除,最终roots链表中全部为真正的垃圾,最后将这些垃圾清除。

    PHP5.3的垃圾回收算法特性总结:
    1、并不是每次refcount减少时都进入回收周期,只有根缓冲区满额后在开始垃圾回收。
    2、可以解决循环引用问题。
    3、可以总将内存泄露保持在一个阈值以下。

    注意:同步周期回收算法 原文中的3.1节 Pseudocode and Explanatio,对算法有详细描述

    image.png

    3. 与垃圾回收算法相关的PHP配置

    可以通过修改php.ini中的zend.enable_gc来打开或关闭PHP的垃圾回收机制
    或通过调用gc_enable()打开、gc_disable()关闭
    或手工调用gc_collect_cycles()函数强制执行内存回收。

    4. 垃圾回收机制对性能的影响

    PHP中的垃圾回收机制,仅仅在循环回收算法运行时会有时间消耗上的增加。但是在平常的(更小的)脚本中根本就没有性能影响。
    但是,在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。因为总共使用的内存没达到上限,这种好处在长时间运行脚本中尤其明显。

    5. 内存溢出和内存泄漏的区别

    内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
    内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

    相关文章

      网友评论

          本文标题:PHP垃圾回收机制学习记录

          本文链接:https://www.haomeiwen.com/subject/lgncjxtx.html