上周五这个时候到新东家面试,里面有一题,谈谈php的垃圾回收机制(garbage collect,简称GC),当时脑子想到的笼统的概念是当对象不使用时便被回收了。回来后,便想着要把这题给解决掉,今儿上午正好有空,于是抽时间小小的研究了下。
先讲讲session的GC机制,具体的可以看下php.ini中这段介绍
; Defines the probability that the 'garbage collection' process is started
; on every session initialization. The probability is calculated by using
; gc_probability/gc_divisor. Where session.gc_probability is the numerator
; and gc_divisor is the denominator in the equation. Setting this value to 1
; when the session.gc_divisor value is 100 will give you approximately a 1% chance
; the gc will run on any give request.
大意就是GC是依据session中的gc_probability/gc_divisor的概率来启动的,如果gc_probability为1,gc_divisor为1000,那么只有千分之一的概率启动GC,也就是说在1000次的session会话中,只有一次可以启动GC。其中gc_probability和gc_divisor都可以通过配置php.ini中session.gc_probability和session.gc_divisor的值来改变,也可以在php脚本中使用ini_set(session.gc_probability, 1)及ini_set(session.gc_divisor, 100)来重新设定。
在GC启动后,会扫描所有的session信息,用当前时间减去session最后修改的时间,同session.gc_maxlifetime(一般为1440秒,也就是24分钟)参数进行比较,如果生存时间超过gc_maxlifetime,就将该session删除。
再来谈谈变量的GC机制。php可以自动进行内存管理,清除不再需要的对象。php使用了引用计数(reference counting)这种单纯的GC机制。每个对象都内含一个引用计数器,每个引用(reference)连接到对象,计数器加1。当reference离开生存空间或被设为NULL,计数器减1。当某个对象的引用计数器为零时,php知道你将不再需要使用这个对象,释放其所占的内存空间。
下面举几个简单的示例
eg1.
#refcount=1
$a = 'good';
#refcount=2
$b = &$a;
echo $a; // good
echo $b; // good
这例子可以理解为在内存开辟一个空间,值为字符串good,并且一个名为$a的变量指向这块内存,同时php内部有个符号表,用来记录各块内存引用计数,那么此时会将这块内存的引用计数 加 1。接下来$b = &$a,这是引用传值,也就是$b的变量也指向了这块内存,同时引用计数加1,此时为2。
eg2
#refcount=1
$a = 'good';
#refcount=2
$b = &$a;
$a = 'test';
echo $a; // test
echo $b; // test
因为$a和$b都指向同一块内存,因此当把$b的值改了,那么$a的值也就跟着变了。
eg3
#refcount=1
$a = 'good';
#refcount=2
$b = &$a;
#refcount=1
unset($a);
echo $a; // Notice: Undefined variable: a in E:\www\test\1408\15.php on line 16
echo $b; // good
看到这里,有的朋友应该有点疑惑了,为什么$b还有值的。其实unset这个操作只是把$a指向的那块内存的引用给断开了,并且使那块内存在符号表中引用计数减1,但并没有影响到其他指向这块内存的变量,因此$b的值还是good。只有当一块内存在符号表中的引用计数为 0 时,php引擎才会将这块内存回收。
eg4
#refcount=1
$a = 'good';
#refcount=2
$b = &$a;
#refcount=0
$a = null;
echo $a; //
echo $b; //
就简单的谈到这,如果想深入的了解,请参照PHP内核探索:新垃圾回收机制说明。