php垃圾回收机制及内存泄露 - Go语言中文社区

php垃圾回收机制及内存泄露


php的垃圾回收机制

以前一直以为垃圾回收什么的是非常深不可测的知识,后来在做重读php手册的计划时,发现php手册已经对php的回收机制做了较为详细的概述,文档地址:http://php.net/manual/zh/features.gc.php
读完这部分文档之后,个人感觉垃圾回收并不深奥,就只是一个引用计数的概念,当变量的引用数等于0时,就会被销毁。
下面展开讨论一下。

  • 请看第一个例子:
<?php
$a = "hello world";
xdebug_debug_zval($a);

注:代码中xdebug_debug_zval函数是xdebug扩展的函数,需要安装xdebug方可使用,关于xdebug的安装可以参考这篇文章:http://blog.csdn.net/u011250882/article/details/48764915
输出结果如下截图:
php的垃圾回收机制
可能很多朋友对截图中的数据不太明白,其实了解过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。

  • 我们再来看第二个例子:
<?php
$a = "hello world";
$b = $a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');

执行结果如下截图所示:
php垃圾回收机制
可以看到当将ab时,$a的引用次数加1。

  • 再看第三个示例:
<?php
$a = "hello world";
$b = $a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
unset($b);
xdebug_debug_zval('a');
xdebug_debug_zval('b');

执行结果如下所示:
php垃圾回收机制
可以看出,当对使用unset函数操作变量时,删除变量,并将引用计数减1。

上面讨论的都是一些简单变量的情况,下面要探讨一下复杂类型(数组、对象等)的情况。请看下面的一个例子:

<?php
$a = array(
    'b' => 'bilibili',
    'a' => 'acfuc'
);

xdebug_debug_zval('a');

执行结果如下所示:
php垃圾回收机制及内存泄露

从上图中可以看出:,array和 object类型的变量把它们的成员或属性存在自己的符号表中。示例中的代码生成了三个zval变量容器。可以形象的用下图来表示(来自php文档,变量名和key并不完全一致):

php垃圾回收机制及内存泄露

当将数组中的一个值赋值给一个新的key键,会发生什么呢?

<?php
$a = array(
    'b' => 'bilibili',
    'a' => 'acfuc'
);

xdebug_debug_zval('a');

$a['a站'] = $a['a'];
xdebug_debug_zval('a');

执行结果如下所示:
php垃圾回收机制及内存泄露

目前$a的内部构造可形象用下图表示(来自php文档,变量名和key并不完全一致):
php垃圾回收机制及内存泄露

现在我们执行unset操作:

<?php
$a = array(
    'b' => 'bilibili',
    'a' => 'acfuc'
);

xdebug_debug_zval('a');

$a['a站'] = $a['a'];
xdebug_debug_zval('a');

unset($a['a'], $a['b']);
xdebug_debug_zval('a');

执行结果:
php垃圾回收机制及内存泄露

php的内存泄露

上面讲了这么多的内容,并没有说内存泄露的问题,那么在什么情况下php会发生内存泄露呢?
还是以上面的代码为例,当将$a自身引用复制给自身,会发生什么有趣的事情呢?

<?php
$a = array(
    'b' => 'bilibili',
    'a' => 'acfuc'
);

xdebug_debug_zval('a');

$a['r'] = & $a;

xdebug_debug_zval('a');

执行结果如下:
php垃圾回收机制及内存泄露

此时$a的内部构造可以形象的用下图表示(来自php文档,变量名和key并不完全一致):
php垃圾回收机制及内存泄露

现在我们删除$a:

<?php
$a = array(
    'b' => 'bilibili',
    'a' => 'acfuc'
);

xdebug_debug_zval('a');

$a['r'] = & $a;

xdebug_debug_zval('a');

unset($a);
xdebug_debug_zval('a');

执行结果:
php垃圾回收机制及内存泄露

此时的情况可以用下图表示(来自php文档,变量名和key并不完全一致):
php垃圾回收机制及内存泄露

尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。

跟垃圾回收相关的配置与方法

php.ini配置:zend.enable_gc,默认值为on,如果想关闭垃圾回收机制,可以设置为off
函数:
gc_enable–激活循环引用收集器
gc_disable–停用循环引用收集器
gc_collect_cycles–强制收集所有现存的垃圾循环周期(即使在可能根缓冲区还没满时,也能强制执行周期回收。你能调用gc_collect_cycles()函数达到这个目的。这个函数将返回使用这个算法回收的周期数。比如再你打算关闭垃圾回收机制之前,先调用一下这个函数,绝对是个明智的选择)

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u011250882/article/details/49564043
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-27 22:26:54
  • 阅读 ( 1128 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢