PHP的垃圾回收机制

PHP的垃圾回收机制

一定程度上说,垃圾回收机制就是用来擦屁股的。如果用过C语言,那么申请内存的方式是malloc或者是calloc,然后你用完这个内存后,一定不要忘了用free函数去释放掉,这就是传说中手动垃圾回收,一般都是扫地神僧用这种方式

PHP的内存核心管理算法

  一是引用计数,

$a = 'hello'. mt_rand( 1, 1000 );
echo xdebug_debug_zval( 'a');
$b = $a;
echo xdebug_debug_zval( 'a');
$c = $a;
echo xdebug_debug_zval( 'a');
unset( $c );
echo xdebug_debug_zval( 'a');

输出结果 

其中,zval struct结构体用于保存$a,zend_value union联合体用于保存数据内容也就是'hello916'。由于后面又声明了b和c,所以C不得不又在底层给你搞出两个zval struct结构体来。

其中,zval和zend value的结构大概如下:(注意!!!这并不是完整正确的PHP zval和zend_value在C语言中struct和union实现,仅仅是挑出最重点的部分写出来,强调一下:你没有必要一个字不差背诵过zval和zend_value,你只需要知道原理)

zval {

string "a" //变量的名字是a

value zend_value //变量的值

type string //变量是字符串类型

}

zend_value {

string "hello916" //值的内容

refcount 1 //引用计数

}

看到上面两个,如果面试官问你php变量为什么能够保存字符串"123"也能保存数字123,你知道该怎么回答了吧?就答出重点zval中有该变量的类型,当是字符串123的时候,type就是string,此时value指向“123”;当是整数123的时候,zval的type为int,value为123。这就是答题的思想,这很重要!而且,通过C语言都是可以实现的!具体真正的val和zend_value的模样,有兴趣的同学可以去网上搜搜,如果你没有C语言的底子,可能比较吃力!前者是一个struct结构体,后者是一个union联合体!

这个refcount就是传说中的引用计数了,初始化的时候a后面的引用次数为1(注意,正确说法应该是a后面的赋值的数组zend_value引用计数为1,而不是a这个变量zval本身)。然后我们将$b = $a,其实相当于又一个变量指向了这个zend_value,所以refcount变为2,最后将$c = $a,同理,zend_value的refcount再次加1变成了3。然后,我们用unset( $c ),这会儿,C语言要做的就是把$c的zval给KO free掉,但是并不是free zend_value,这会儿zend_value的refcount就自然而然减1变成2了。

二是写时拷贝

<?php

// 先不要问为什么非要加mt_rand,不然,绝笔说不过来了,到处都是坑

$a = 'hello'. mt_rand( 1, 1000 );

$b = $a;

$a = 123;

echo $b. PHP_EOL;

// 运行结果,不用我说吧,脚趾头都知道是'hello'.mt_rand( 1, 1000 )的结果,绝对不可能是123。

其实,当你把$a赋值给$b的时候,$a的值并没有真的复制了一份,这样是对内存的极度不尊重,也是对时间复杂度的极度不尊重,计算机仅仅是将$b指向了$a的值而已,这就叫多快好省。那么,什么时候真正的发生复制呢?就是当我们修改$a的值为123的时候,这个时候就不得已进行复制,避免$b的值和$a的一样。

<?php

$a = 'hello'. mt_rand( 1, 1000 );

$b = $a;

echo xdebug_debug_zval( 'a');

$a = 'world'. mt_rand( 2, 2000 );

echo xdebug_debug_zval( 'a');

// 运行结果为1,其中的原理你自己应该能理顺了昂

叨逼叨了这么长,通过简单的案例解释清楚了两个要点:引用计数和写时拷贝,那么垃圾回收也该来了。当一个zval在被unset的时候、或者从一个函数中运行完毕出来(就是局部变量)的时候等等很多地方,都会产生zval与zend_value发生断开的行为,这个时候zend引擎需要检测的就是zend_value的refcount是否为0,如果为0,则直接KO free空出内容来。如果zend_value的recount不为0(废话一定是大于0),这个value不能被释放,但是也不代表这个zend_value是清白的,因为此zend_value依然可能是个垃圾

原文地址:https://www.cnblogs.com/zh718594493/p/12853155.html