php的垃圾回收机制
以前一直以为垃圾回收什么的是非常深不可测的知识,后来在做重读php手册的计划时,发现php手册已经对php的回收机制做了较为详细的概述,文档地址:http://php.net/manual/zh/features.gc.php
读完这部分文档之后,个人感觉垃圾回收并不深奥,就只是一个引用计数的概念,当变量的引用数等于0时,就会被销毁。
下面展开讨论一下。
- 请看第一个例子:
- 1
- 2
- 3
注:代码中xdebug_debug_zval函数是xdebug扩展的函数,需要安装xdebug方可使用,关于xdebug的安装可以参考这篇文章:http://blog.csdn.net/u011250882/article/details/48764915
输出结果如下截图:
可能很多朋友对截图中的数据不太明白,其实了解过php底层的朋友都知道(这句话很装逼,因为我并不了解php的底层,笑),每个php变量都存在一个叫”zval”的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是”is_ref”,是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用。第二个额外字节是”refcount”,用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope),那些主脚本(比如:通过浏览器请求的的脚本)和每个函数或者方法也都有作用域。
默认情况下,一个新声明的变量is_ref为false,refcount的数值为1。而且当refcount的数值为1的情况下,is_ref的值一定为false。
- 我们再来看第二个例子:
- 1
- 2
- 3
- 4
- 5
执行结果如下截图所示:
可以看到当将
- 再看第三个示例:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
执行结果如下所示:
可以看出,当对使用unset函数操作变量时,删除变量,并将引用计数减1。
上面讨论的都是一些简单变量的情况,下面要探讨一下复杂类型(数组、对象等)的情况。请看下面的一个例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
执行结果如下所示:
从上图中可以看出:,array和 object类型的变量把它们的成员或属性存在自己的符号表中。示例中的代码生成了三个zval变量容器。可以形象的用下图来表示(来自php文档,变量名和key并不完全一致):
当将数组中的一个值赋值给一个新的key键,会发生什么呢?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
执行结果如下所示:
目前$a的内部构造可形象用下图表示(来自php文档,变量名和key并不完全一致):
现在我们执行unset操作:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
执行结果:
php的内存泄露
上面讲了这么多的内容,并没有说内存泄露的问题,那么在什么情况下php会发生内存泄露呢?
还是以上面的代码为例,当将$a自身引用复制给自身,会发生什么有趣的事情呢?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
执行结果如下:
此时$a的内部构造可以形象的用下图表示(来自php文档,变量名和key并不完全一致):
现在我们删除$a:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
执行结果:
此时的情况可以用下图表示(来自php文档,变量名和key并不完全一致):
尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。
跟垃圾回收相关的配置与方法
php.ini配置:zend.enable_gc,默认值为on,如果想关闭垃圾回收机制,可以设置为off
函数:
gc_enable–激活循环引用收集器
gc_disable–停用循环引用收集器
gc_collect_cycles–强制收集所有现存的垃圾循环周期(即使在可能根缓冲区还没满时,也能强制执行周期回收。你能调用gc_collect_cycles()函数达到这个目的。这个函数将返回使用这个算法回收的周期数。比如再你打算关闭垃圾回收机制之前,先调用一下这个函数,绝对是个明智的选择)