垃圾回收机制

这篇文章主要介绍了PHP之垃圾回收机制,详细分析了php中垃圾回收机制的概念、原理及对性能的影响,需要的朋友可以参考下

垃圾回收(机智)

要说起垃圾回收机制,听别人说的最对的就是引以计数,当时还不太明白,以为就是计数这么简单。直到不久前的一个面试,面试官问我,你知道php的垃圾回收机制吗,我自信的回答:知道,垃圾回收用到了引以计数,答道这里面试官还较满意。又问:那它是怎么进行回收的?我认真的思考了一下说:就是把用过之后不用了的变量采用引以计数,归到一块,等到十个或者二十个的时候进行销毁释放。正当我准备迎接面试官下个问题的时候,却迎来了一句:get out 。now!!我还不明所以,愣了一下,面试官又说了一句:“带上简历,出门左转”。(出门左转是人家公司门口)
在这里插入图片描述

什么是垃圾回收

垃圾回收,简称gc。顾名思义,就是废物重利用的意思。是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。在现在的流行各种语言当中,垃圾回收机制是新一代语言所共有的特征,如Python、PHP、C#、Ruby等都使用了垃圾回收机制。

PHP的垃圾回收机制

一、在PHP5.3版本之前,使用的垃圾回收机制是单纯的“引用计数”。即:
①每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;
②当变量引用撤掉后(执行unset()后),计数器-1;
③当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。
并且PHP在一个生命周期结束后就会释放此进程/线程所占的内容,这种方式决定了PHP在前期不需要过多考虑内存的泄露问题。

二、php5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露。
1、 每个php变量存在一个叫"zval"的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息refcount和is_ref ,refcount是引用计数 ,标识此zval被多少个变量引用 , 为0时会被销毁
is_ref标识是否使用的 &取地址符强制引用。

2、为了解决循环引用内存泄露问题 , 使用同步周期回收算法
比如当数组或对象循环的引用自身 , unset掉数组的时候 , 当refcount-1后还大于0的 , 就会被当成疑似垃圾 , 会进行遍历 ,并且模拟的删除一次refcount-1如果是0就删除 ,如果不是0就恢复

<?php
	$a = "new string";
	xdebug_debug_zval( 'a' );
	$b = $a;
	xdebug_debug_zval( 'a' );
	unset($b);
	xdebug_debug_zval( 'a' );
	$c = $b = $a;
	xdebug_debug_zval( 'a' );
?>

当把$a赋值给另外一个变量时, $a对应的zval的refcount会加1

当用unset删除$b,变量时,“new string” 的refcount会减1变成1。

以上的例程结果:

	a: (refcount=1, is_ref=0)='new string'
	a: (refcount=2, is_ref=0)='new string'
	a: (refcount=1, is_ref=0)='new string'
	a: (refcount=3, is_ref=0)='new string'

注意:从PHP7的NTS版本开始,以上例程的引用将不再被计数,即 $c = $b = $a之后a的引用计数也是1.具体分类如下:
在PHP 7中,zval可以被引用计数或不被引用。在zval结构中有一个标志确定了这一点。
①对于null,bool,int和double的类型变量,refcount永远不会计数;
②对于对象、资源类型,refcount计数和php5的一致;
③对于字符串,未被引用的变量被称为“实际字符串”。而那些被引用的字符串被重复删除(即只有一个带有特定内容的被插入的字符串)并保证在请求的整个持续时间内存在,所以不需要为它们使用引用计数;
④对于数组,未引用的变量被称为“不可变数组”。其数组本身计数与php5一致,但是数组里面的每个键值对的计数,则按前面三条的规则(即如果是字符串也不在计数);
我们再来demo例子:

<?php
	echo '测试字符串引用计数';
	$a = "hello world";
	$b = $a;
	xdebug_debug_zval( 'a' );
	unset($b);
	xdebug_debug_zval( 'a' );
	$b = &$a;
	xdebug_debug_zval( 'a' );
	echo '测试数组引用计数';
	$c = array('meaning' => 'life', 'number' => 42);
	xdebug_debug_zval( 'c' );
	$d = $c;
	xdebug_debug_zval( 'c' );
	$c['name']='Tom';
	xdebug_debug_zval( 'c' );
	echo '测试int型计数';
	$e = 1;
	xdebug_debug_zval( 'e' );

以上的输出结果如下:
在这里插入图片描述
如果在上边的$c中添加一个元素,并将现有元素的值赋给新的元素 :

<?php
    $c = array('meaning' => 'life', 'number' => 42);
    $c['name'] = $c['meaning'];
    xdebug_debug_zval( 'c' );
?>

那么$c的内部储存结果是:

	c: (refcount=1, is_ref=0)=array (
		'meaning' => (refcount=2, is_ref=0)='life',
		'number' => (refcount=1, is_ref=0)=42,
		'name' => (refcount=2, is_ref=0)='life'
	)

但是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露的现象。

什么又是环形引用

举个例子:

<?php
    $a = array('one');
    $a[] = &$a;  //引用了自己
?>

这样$a 数组就有两个元素,一个索引为0,值为字符one,另外一个索引为1,为 $a自身的引用,
如图所示:
在这里插入图片描述

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

array这个zval的refcount是2 , 是一个环形引用
这时对 $a 进行unset,那么 $a 会从符号表中删除,同时 $a 指向的zval的refcount_gc减少1.

<?php
	$a = array('one');
	$a[] = &$a;
	unset($a);
?>

那么问题就产生了,$a 已经不在符号表中,用户无法再访问此变量,但是 $a 之前指向的zval的refcount_gc变为1而不是0,因此不能被回收,从而产生内存泄露,新的GC要做的工作就是清理此类垃圾。

为了解决循环引用内存泄露问题 , 使用同步周期回收算法 , 这种ref_count减1后还大于0的会被作为疑似垃圾。比如:当数组或对象循环的引用自身 , unset掉数组的时候 , 当refcount-1后还大于0的 , 会进行遍历 ,并且模拟的删除一次refcount-1如果是0就删除 ,如果不是0就恢复

现在想想之前的面试:啥也不是 , 该打 该打!!

在这里插入图片描述

回收周期

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

除了修改配置zend.enable_gc ,也能通过分别调用gc_enable() 和 gc_disable()函数在运行php时来打开和关闭垃圾回收机制。调用这些函数,与修改配置项来打开或关闭垃圾回收机制的效果是一样的。即使在可能根缓冲区还没满时,也能强制执行周期回收。你能调用gc_collect_cycles()函数达到这个目的。这个函数将返回使用这个算法回收的周期数。

性能影响

1、内存占用空间的节省
首先,实现垃圾回收机制的整个原因是为了一旦先决条件满足,通过清理循环引用的变量来节省内存占用。在PHP执行中,一旦根缓冲区满了或者调用gc_collect_cycles() 函数时,就会执行垃圾回收。

2、执行时间增加
垃圾回收影响性能的第二个领域是它释放已泄漏的内存耗费的时间。
通常,PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加。但是在平常的(更小的)脚本中应根本就没有性能影响。

3、在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。因为总共使用的内存没达到上限。


文章:以上就是垃圾回收机制,感谢各位大佬的阅读、点赞、评论,希望可以帮助到你们。我是 敲码农的小Bug 我们下期见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值