PHP GC Garbage Cycle 垃圾回收
基础知识
垃圾:当一个对象没有任何引用指向他的时候就是垃圾(需要释放的内存);也就是当计数器为0的时候,会销毁这个变量,所以这里的垃圾并不能被称为垃圾,这里的垃圾应该是可以成功释放内存,不能被销毁的才是垃圾,和上面需要释放内存的垃圾不是一个意思,要区分开;
定位垃圾的两种算法:
- reference count 引用计数 计数是0的时候就是垃圾 不能解决循环引用的问题;循环引用会导致内存的泄露;php使用的就是这种算法;php5.3对GC优化,来解决循环引用的问题;
- root searching 根可达算法,就是从根开始先找到不是垃圾的,然后删除所有的垃圾,可以解决循环引用的问题;例如java;
优点:自动内存回收编程简单,系统不容易出错误;
引入两个概念:
- 内存泄漏:程序申请内存后无法释放已经申请的内存;一次内存泄漏并不会产生很大的影响,但是内存的泄漏堆积就会形成内存溢出;
- 内存溢出:程序申请内存,没有足够的内存供给使用者。
变量容器:
- 看成内存,指向该内存的变量,存放变量数据的地方;
- 当一个变量被赋值的时候,那么就会生成一个zval的变量容器;
- 复合类型,array,object类型的变量,会把他们的成员或属性存在自己的符号表,所以一个变量会有多个zval容器;
测试工具:
xdebug_debug_zval() //好像是需要安装xdebug的插件;
- refcount : 指向该变量容器的变量数;
- is_ref : 变量是不是被引用;
- 注意 上面两个参数的操作对象是变量容器;
php的zval结构体:
struct _zval_struct {/* Variable information */zvalue_value value; /* value */zend_uint refcount__gc; //代表一个计数器,表示有多少个变量名指向这个zval变量容器zend_uchar type; /* active type */zend_uchar is_ref__gc; //此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎可以区分一般变量和引用变量};
- refcount :对象是zval变量容器的变量个数;
- is_ref:变量是否被引用;
- 上面的zval也是php弱关系语言的原因;
- php的变量类型的判断就是依靠type;
zvalue_value的联合体
struct _zval_struct {union {long lval;double dval;struct {char *val; //存放的是指针int len; //php直接保存字符串的str,所以strlen的时间复杂度是O(1)} str;HashTable *ht; //数组 存放的是地址 指针zend_object_value obj; //对象 php5 这里是一个指针,php3,php4,赋值的时候需要赋值整个对象(copy),所以效率非常低,在php5做了优化,跟随java的步伐,采用了指针或者句柄的形式;} value; //变量value值zend_uint refcount__gc; //引用计数内存中使用次数,为0删除该变量zend_uchar type; //变量类型zend_uchar is_ref__gc; //区分是否是引用变量};
弱关系语言和强关系语言
- 弱关系语言:一个变量可以表示任意的数据类型,不需要声明变量类型;php rust javascript
- 强关系语言:一个变量被申明为某一个变量类型,在运行过程中,不能把该类型外的值赋给他;java c
写(赋值的意思)时复制 copy
$a = 123; // 赋值的时候创建一个zval容器变量$b = $a; //$b 和 $b指向同一个zval的容器变量 refcount =2$b = 34; //$a $b分别指向两个容器变量refcount =1
垃圾回收
php的垃圾回收很简单,就是refcount = 0时候会进行内存回收,也可以手动的unset进行回收;
循环引用的解决方案
什么是循环引用
$a = array(\'a\',\'b\');$[]=$a;unset($a); //会形成一个闭环,造成循环引用;
php5.3之后引入了根缓存机制,来解决循环引用的问题,设定了zval的根缓冲区(默认是1000),当满1000的时候就会对垃圾回收,来解决内存泄漏的问题;
几种可疑垃圾的准则:
- 计数减少到0,free,肯定不是垃圾;
- 计数增加也不是垃圾,该容器变量还在用;
- 只有在计数减少到非零时,才会进入到垃圾周期;(collecting cycle)
在垃圾周期内,通过检查引用计数是否减1,并且检查那些变量容器的引用次数是零,来发现那部分是垃圾;
紫色看成可疑垃圾;
其实上面的过程简单点说就是:模拟删除一下可疑垃圾,如果引用计数减1,变成0,那么就变成灰色,c代表的是模拟恢复,就是可疑垃圾计数变大于0就要对他恢复,D就是真实的删除;
还有一个很大的误区:因为php有GC,所以不需要手动fclose关闭资源,反正请求结束后php会释放所有的资源,所以没必要去手动关闭;有这样思想的,建议烧书,哈哈哈哈;详细具体看:click
参考书籍 《深入php内核》