今天朋友去面试,回来问了一下怎么样,结果他说一脸懵逼,看来我们平时还是学习的太少了啊。于是比较好奇,果断问了一下都有哪些问题,朋友说第一个问题就是“描述PHP的垃圾回收机制”,我当时听了也是一脸茫然,因为平时我们业务逻辑写的太多,很少去关注这些,但是没办法,既然有人问这个问题,看来还是很有必要了解一下的。于是马上搜了一下,网上资料文章很多,看了几篇后加上自己的一些理解记录一下。

  首先看了一下官方手册,只有php5.3版本以后的才有了所谓的新的垃圾回收机制GC,那么以前是怎么干的呢?以前是基于引用计数的方式,这里就需要提一下引用计数的知识,官方手册里面说php的每个变量都是存在一个叫做zval的容器里面,这个容器不仅包含了这个变量的值和类型,还包含了另外两个重要的信息,“is_ref”和“refcount”,“is_ref”看名字就应该知道大概和引用相关,它是一个bool值,如果这个值是true那么代表这是一个引用变量,否则是普通变量。“refcount”指的是有多少个变量(符号)指向这个zval容器。

  比如一个变量$a="test",如果我们php安装了xdebug插件并且开启了插件,就可以用xdebug_debug_zval(“a”)来显示zval里面的值。这里会输出a:(refcount=1,is_ref=0)=“test”,可以看到refcount=1,因为这里有一个变量(符号)$a指向了这个zval容器,is_ref=0说明这个存放的是一个普通变量。

  如果我们进行一个操作$b=$a呢?按照常规的思路,应该是把$a的值复制一份给$b,然后$b也存放在另一个zval容器中,这个zval容器内容和$a那个一样。真的是这样吗?我们用xdebug_debug_zval(“a”)先输出$a对应的zval容器值,结果会输出a:(refcount=2,is_ref=0)="test",这里refcount变成了2 ,说明除了$a还有一个变量(符号)指向这个zval容器,那就是$b了啊,这么一来$a和$b指向的是同一个zval容器,那不是修改$b也会影响到$a了?其实不会的,因为当$b或者$a的值改变的时候,这个zval容器的refcount会减一,然后会复制一份让改变值的那个变量(符号)指向新的zval容器,这个时候就是我们刚才常规思路想的一样了,有了两个zval容器都是(refcount=1,is_ref=0)只是两个容器的值和类型分别是$a和$b的值和类型。

  那如果是引用赋值$c=&$a呢?这时$a和$c同样也指向同一个zval,即a,c:(refcount=2,is_ref=1)="test",这时候不光refcount加一,is_ref也变成了1也就是true,说明这是引用变量,那么改变$a和$c任何一个都会影响另一个的值。我们如果使用unset($c)的话,$a指向的容器的refcount就会减一变成1。如果我们再unset($a)的话,指向的zval容器的refcount就是0了,这个时候说明已经没有变量(符号)指向这个容器了,那么php引擎就会从内存中销毁释放这个容器。

  那如果$a是一个数组呢,它指向的zval容器会是怎样的?比如$a=array("1","2"),xdebug_debug_zval(“a”)会输出如下的信息:

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

  可以看到除了$a本身指向一个zval容器存放外,它的每一个元素也都分别指向一个zval容器,如果我要这样往$a中添加元素会怎样?

$a = array( 'meaning' => 'life', 'number' => 42 );
$a['life'] = $a['meaning']; //这里直接拿官方示例

  这个时候xdebug_debug_zval(“a”)会输出: key为'meaning'和'life'的值指向同一个zval容器,refcount=2

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'
)

  如果我们在添加元素的时候,添加的是对数组本身的引用,又会变成什么样?

<?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数组本身指向的容器refcount变成了2,因为$a和$a[1]指向了这个容器,然而$a[1]又是$a的元素,这个元素又引用了$a本身,这就形成了一个闭环。

  如果这个时候来一句unset($a)呢?$a指向的容器refcount减一就会变成1,这个时候对于我们程序员来说已经不存在有可操作的变量(符号)指向这个容器了,但是refcount=1那么php引擎就不会销毁这个容器。

  那不是这个容器在内存中不就成了垃圾了吗?这种情况如果没有垃圾回收机制GC,那么就只有等到当前请求结束,脚本结束自动清除了。但是有时候我们会用到一些递归或者死循环这类的来做一些特殊的业务逻辑,这时候内存如果有上面的情况出现,就会导致内存泄漏,消耗很大的内存空间。

  所以才有了5.3版本新的内存回收机制的出现。先说说机制的三个基本规则:

  • 如果一个zval容器的refcount增加,说明有新的变量(符号)指向这个容器,那么这个容器当然不会是垃圾,它将被继续使用。
  • 如果一个zval容器的refcount减少到0了,那么说明没有变量(符号)指向这个容器,它就会被php引擎销毁。
  • 如果一个zval容易的refcount减少了,但是不是0,那么这个容器就有可能是垃圾,就会被垃圾回收机制所管理。

  怎么管理这些容器并判断哪些是垃圾呢?当发现某个容器有可能是垃圾时,这个容器会被放进一个内存缓冲区,当缓冲区满了时,就会进行垃圾回收算法来找出垃圾并销毁。这里具体的算法可以看看官方文档,我就用一个网友的总结来描述:

  对于一个包含环形引用的数组,对数组中包含的每个元素的zval进行减1操作,之后如果发现数组自身的zval的refcount变成了0,那么可以判断这个数组是一个垃圾。

  这个道理其实很简单,假设数组a的refcount等于m, a中有n个元素又指向a,如果m等于n,那么算法的结果是m减n,m-n=0,那么a就是垃圾,如果m>n,那么算法的结果m-n>0,所以a就不是垃圾了。m=n代表什么?  代表a的refcount都来自数组a元素的指向,代表除了a中的元素没有任何变量(符号)指向根zval容器,代表用户代码空间中无法再访问到这个zval,代表a是泄漏的内存,因此GC将a这个垃圾回收了。

  最后在哪里可以设置这个回收机制呢?默认的,PHP的垃圾回收机制是打开的,然后在配置文件 php.ini 里允许你修改它:zend.enable_gc 。除了修改配置zend.enable_gc是否开启 ,也能通过分别调用gc_enable() 和 gc_disable()函数来打开和关闭垃圾回收机制。调用这些函数,与修改配置项来打开或关闭垃圾回收机制的效果是一样的。如果想在根缓冲区还没满时强制执行周期回收,可以调用gc_collect_cycles()函数,这个函数将返回使用这个算法回收的周期数。

  当垃圾回收机制打开时,每当根缓存区存满时,就会执行查找算法。根缓存区有固定的大小,可存10,000个可能根,当然可以通过修改PHP源码文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,然后重新编译PHP,来修改这个10,000值。当垃圾回收机制关闭时,循环查找算法永不执行,然而,可能根将一直存在根缓冲区中,不管在配置中垃圾回收机制是否激活。

  当垃圾回收机制关闭时,如果根缓冲区存满了可能根,更多的可能根显然不会被记录。那些没被记录的可能根,将不会被这个算法来分析处理。如果他们是循环引用周期的一部分,将永不能被清除进而导致内存泄漏。

  即使在垃圾回收机制不可用时,可能根也被记录的原因是,相对于每次找到可能根后检查垃圾回收机制是否打开而言,记录可能根的操作更快。不过垃圾回收和分析机制本身要耗不少时间。

  也就是说关闭了回收机制也会往缓冲区丢疑似垃圾的容器,当缓冲区满了的时候不会执行回收算法,后面更多的疑似垃圾容器不会继续放进去,就可能导致内存泄漏。当开启回收机制后,就会从缓冲区中之前放入的容器中开始垃圾回收机制。

php内存回收机制的学习的更多相关文章

  1. Java技术专题之JVM逻辑内存回收机制研究图解版

    一.引言 JVM虚拟机内存回收机曾迷惑了不少人,文本从JVM实现机制的角度揭示JVM内存回收的原理和机制. 一.Java平台逻辑架构 二.JVM物理结构 通过从JVM物理结构图我们可以看到: 1.JV ...

  2. Java进阶3. 内存回收机制

    Java进阶3. 内存回收机制 20131029 前言: 学过C++的都知道,C++中内存需要程序员自己维护.说道这里,很多开发的同学就感觉很痛苦,当他转向Java的时候,就会说你看Java多好啊,程 ...

  3. Android 操作系统的内存回收机制(转载)

    Android 操作系统的内存回收机制(转载) Android APP 的运行环境 Android 是一款基于 Linux 内核,面向移动终端的操作系统.为适应其作为移动平台操作系统的特殊需要,谷歌对 ...

  4. JVM内存回收机制简述

    JVM内存回收机制涉及的知识点太多了,了解越多越迷糊,汗一个,这里仅简单做个笔记,主要参考<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)> 目前java的jdk默认虚拟机为H ...

  5. memcache的内存回收机制

    memcache不会释放内存,而是重新利用. 在缓存的清除方面,memcache是不释放已分配内存.当已分配的内存所在的记录失效后,这段以往的内存空间,memcache只会重复利用. memcache ...

  6. JVM内存回收机制

    1. JVM内存回收机制简述 http://www.cnblogs.com/lzrabbit/p/3826738.html

  7. Android 操作系统的内存回收机制[转]

    转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-mmry-rcycl/ Android APP 的运行环境 Andro ...

  8. Redis的内存回收机制

    Redis的内存回收机制 2018年01月16日 17:11:48 chs007chs 阅读数:1172   Redis的内存回收机制主要体现在一下两个方面: 删除过期时间的键对象 删除过期键对象 : ...

  9. Qt ------ 内存回收机制、new对象的回收

    写在前面的总结: 建议:对于不能指定父对象的对象(对象通过moveToThread()移入其他线程.没有继承QObject的类产生的对象),在其他线程通过deleteLater()内存回收,其他通过指 ...

随机推荐

  1. pip模块的使用

    安装pip: sudo apt-get install python-pip           (python2的安装) sudo apt-get install python3-pip      ...

  2. HDU 2546:饭卡(01背包)

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  3. .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件

    你可以使用临界区(Critical Section).互斥量(Mutex).信号量(Semaphores)和事件(Event)来处理线程同步.然而,在编写一些异步处理函数,尤其是还有 async 和 ...

  4. selenium(一)--selenium 家族

    2015-12-16 23:17:04 QTP mercury 是商业的,单词意思是水银,而selenium 是开源的,单词意思是硒,有些相对的意思. 1.selenium ide selenium ...

  5. juc并发工具类之CountDownLatch闭锁

    import java.util.concurrent.CountDownLatch; /** * 闭锁: 在进行某些运算时, 只有其他所有线程的运算全部完成,当前运算才继续执行(程序流中加了一道栅栏 ...

  6. Java的四种引用之强弱软虚

    在java中提供4个级别的引用:强引用.软引用.弱引用和虚引用.除了强引用外,其他3中引用均可以在java.lang.ref包中找到对应的类.开发人员可以在应用程序中直接使用他们. 1 强引用 强引用 ...

  7. HBase常用指令

    disable 'smsFlow'drop 'smsFlow'create 'smsFlow','info','partition'count 'smsFlow'scan 'smsFlow' trun ...

  8. 使用JMeter代理服务器录制APP脚本

    重点:证书的安装,需要将Jmeter安装目录下证书传送到手机,使用手机安装(不要用QQ传送给手机,手机提示无法安装,可使用网盘方式传送,可成功安装证书) (出现该错误时,需安装证书) 简单的配置教程如 ...

  9. hello world之Makefile

    hello world之Makefile

  10. DBA的规范

    DBA操作规范 1.涉及业务上的修改/删除数据,在得到业务方.CTO的邮件批准后方可执行,执行前提前做好备份,必要时可逆.   2.所有上线需求必须走工单系统,口头通知视为无效.   3.在对大表做表 ...