详解php的垃圾回收机制

首先php是由C语言编写,C语言是强类型语言,每个变量都有固定的类型,但是php变量可以保存任何数据类型,是怎么实现的呢?zend引擎中是怎么可以做到一个变量保存任何的数据类型?
每一个变量定义的时候都会保存在一个叫zval的容器中

struct_zval_struct{
    zvalue_value value;   //变量的值,这个成员变量是一个zvalue_value联合体,php可以将任何类型保存在这个结构体
    zend_uint refcount;   //变量引用数,变量引用计算器
    zend_uchar type;      //变量的类型,当类型为整型时,就可以去zvalue_value结构体中拿lval,其他的同理
    zend_uchar is_ref;    //变量是否被引用
}
//zvalue_value结构体
typeof union_zvalue_value{
    long lval;
    double dval;
    struct{
       char *val;
       int len;
    }str
    HashTable *ht;          
    zend_object_value_obj;     //对象类型
}zvalue_value

看到上面两个,如果面试官问你php变量为什么能够保存字符串"123"也能保存数字123,你知道该怎么回答了吧?就答出重点zval中有该变量的类型,当是字符串123的时候,type就是string,此时value指向“123”;当是整数123的时候,zval的type为int,value为123。这就是答题的思想。

php内存管理的核心算法就是:引用计数器和写时拷贝
1.引用计数器 refcount

$a = 'this is test';
xdebug_debug_zval('a');      //refcount = 1 ,is_ref = 0 
$b=$a;
xdebug_debug_zval('a');     //refcount = 2 ,is_ref = 0
$c = $a;
echo xdebug_debug_zval( 'a');    //refcount = 3 ,is_ref = 0
unset( $c );  //只是unset了一个指向,内存块还在
echo xdebug_debug_zval( 'a');    //refcount = 2 ,is_ref = 0

因为$b并不是引用变量$a,所以$a的is_ref字段值为false
$a = 0;
xdebug_debug_zval('a');      //refcount = 1 ,is_ref = 0  int(1)
$b = & $a;
xdebug_debug_zval('a');     //refcount = 2 ,is_ref = 1 int(1)
$b += 5;
xdebug_debug_zval('a');    //refcount = 2 ,is_ref = 0   int(6)

&才是引用。

2.写时复制

$a = 'this is test';
xdebug_debug_zval('a');      //refcount = 1 ,is_ref = 0
$b=$a;
xdebug_debug_zval('a');     //refcount = 2 ,is_ref = 0
$a = 'aaaaaa';
echo xdebug_debug_zval( 'a');    //refcount = 1 ,is_ref = 0

写时复制是一个解决内存复用的方法。若简单的把$a赋值给$b,就有两个this is test 字符串的复制,这样不利于内存复用。
内存中还有相同的值。所以这样很耗内存。写时复制就是当变量的值改变时才进行内存的复制。当变量a赋值给b时,这时候指向统一块内存块,refcount加1.当变量a改变时,refcount变回1,两个指向不同的内存块。当其中一个值发生变化时,才会创建内存块去保存新的值。其实refcount的值就是看有多少个变量指向这个内存块,少一个指向就减1。

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

什么样的情况会导致zend_value的refcount不为0,但是这个zend_value却是个垃圾呢?PHP7种两种情况:

<?php $arr = [ 1 ]; $arr[] = &$arr; unset( $arr ); 这种情况下,zend_value不会能释放,但也不能放过它,不然一定会产生内存泄漏,所以这会儿zend_value会被扔到一个叫做垃圾回收堆中,然后zend引擎会依次对垃圾回收堆中的这些zend_value进行二次检测,检测是不是由于上述两种情况造成的refcount为1但是自身却确实没有人再用了,如果一旦确定是上述两种情况造成的,那么就会将zend_value彻底抹掉释放内存。 那么垃圾回收发生在什么时候?有些同学可能有疑问,就是php不是运行一次就销毁了吗,我要着gc有何用?并不是啦,首先当一次fpm运行完毕后,最后一定还有gc的,这个销毁就是gc;其次是,内存都是即用即释放的,而不是攒着非得到最后,你想想一个典型的场景,你的控制器里的某个方法里用了一个函数,函数需要一个巨大的数组参数,然后函数还需要修改这个巨大的数组参数,你们应该是函数的运行范围里面修改这个数组,所以此时会发生写时拷贝了,当函数运行完毕后,就得赶紧释放掉这块儿内存以供给其他进程使用,而不是非得等到本地fpm request彻底完成后才销毁。 比较好的链接:https://blog.csdn.net/weixin_30284355/article/details/99015373?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值