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

    什么是垃圾?
    垃圾主要是针对内存的,如果一个对象struct,并没有任何变量引用它,那这个对象就是垃圾。

    为啥要清理垃圾?
    不是说php线程结束的时候回销毁所有的变量,关闭所有打开的句柄资源,不都是自动的吗?为什么还需要清理?
    ·如果php开启了很多内存空间,但是却没有销毁它,内存会一点点的被吃掉,最终导致内存溢出。
    ·如果写的php代码是个需要长时间执行的呢,例如弄成守护进程。

    如何回收垃圾?
    第一步:从所有变量中找到垃圾(所有的变量怎么来的?怎样才能知道其中的某个变量是垃圾?)
    第二步:找到垃圾后,清除垃圾(怎么清除?怎样才算真的清除垃圾?)

    1、如何找垃圾
    从所有变量中找到垃圾,所有的变量get_defined_vars函数能够看到所有已经定义的变量,那意味着php本身能存储所有变量或已开辟的内存空间,具体在zend_globals.h中struct _zend_executor_globals中看到

    补充:php变量值存储在struct _zval_struct中,而变量名存储在struct _zend_executor_globals中
    struct _zend_executor_globals {

    /*略*/

    HashTable *active_symbol_table; /* 局部变量的符号表 */
    HashTable symbol_table; /* main symbol table 全局变量的符号表*/

    /*略*/

    这样就能找到所有的变量了,怎样找到垃圾?按照我们的想法,一定会在这个变量上面打个标签,这个是垃圾,可以清理了。那么这个标签在哪里呢?
    在struct _zval_struct中
    zend_uint refcount__gc //用来标记有多少个变量指向它
    zend_uchar is_ref__gc //用来标记是否用引用的方式指向它

    2、如何清理掉
    看看是不是垃圾,就看refcount__gc是不是0,找到0就清除,并回收内存(5.2以及以前版本)
    在php5.3以后,采用了新的算法,即引用计数系统中的同步周期回收算法来清除

    先看一个简单的例子:
    <?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)=...
    )


    能看到数组变量(a)同时也是这个数组的第二个元素(1)指向的变量容器中"refcount"为2。上面的输出结果中的"..."说明发生了递归操作,显然在这种情况下意味着"..."指向原始数组。
    跟刚刚一样,对一个变量调用unset,将删除这个符号,且它指向的变量容器中的引用次数也减1.所以,如果我们执行完上面的代码后,对变量$a调用unset,那么变量$a和数组元素“1”所指向的变量容器的引用次数减1,从“2”变成“1”,如下:
    (refcount=1,is_ref=1)=array(
    0=>(refcount=1,is_ref=0)='one',
    1=>(refcount=1,is_ref=1)=...
    )

    3、引发思考
    尽管不在有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。庆幸的是,php将在脚本执行结束时清除这个数据结构,但是在php清除之前,将想好不少内存。
    如果上面的情况发生仅仅一两次倒没什么,但是如果出现几千次,甚至几十万次的内存泄漏,这显然是个大问题。这样的问题往往发生在长时间运行的脚本中,比如请求基本上不会结束的守护进程或者单元测试中的大的套件中。

    4、php5.3的删除算法介绍
    在php5.3中,采用模拟删除每个疑似变量。模拟删除时将变量引用数减1,然后再进行模拟恢复,但恢复是有条件的,当有其他变量指向该值时才对其做模拟恢复。这样剩下的一对没能恢复的就是该删除的

    5、什么时候回收?
    ·当我们存储的疑似垃圾的区域满了的时候,就会被执行清除垃圾的操作,前提是开启了垃圾回收机制,当然默认是打开的,php.ini设置允许你修改它:zend.enable_gc
    ·修改配置zend.enable_gc,也能通过分别调用gc_enable()和gc_disable()函数来打开和关闭垃圾回收机制。调用这些函数,与修改配置项来打开挥着关闭垃圾回收机制的效果是一样的。你还能调用gc_collect_cycles()函数达到这个目的。他能强制执行周期回收。

  • 相关阅读:
    Python 存储引擎 数据类型 主键
    Python 数据库
    Python 线程池进程池 异步回调 协程 IO模型
    Python GIL锁 死锁 递归锁 event事件 信号量
    Python 进程间通信 线程
    Python 计算机发展史 多道技术 进程 守护进程 孤儿和僵尸进程 互斥锁
    Python 异常及处理 文件上传事例 UDP socketserver模块
    Python socket 粘包问题 报头
    Django基础,Day7
    Django基础,Day6
  • 原文地址:https://www.cnblogs.com/lxhyty/p/11308571.html
Copyright © 2011-2022 走看看