PHPER必了解——PHP GC机制

前言

  • PHP5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器。
    只是简单的判断了一下变量的zval的refcount是否为0,是的话就释放否则不释放直至进程结束。
  • PHP5.3出现了专门负责清理垃圾数据、防止内存泄漏的GC。

PHP变量 zval 容器

每个php变量存在一个叫"zval"的变量【zval容器】中
name:字段值
is_ref:标识这个变量是否是属于引用集合变量
refcount:表示指向这个zval变量容器的变量(也称符号即symbol)个数
当被变量引用时refcount+1,当变量撤掉时refcount-1,当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。

变量引用示例

  1. refcount计数减1,unset并非一定会释放内存,当有两个变量指向的时候,并非会释放变量占用的内存,只是refcount减1
    【变量销毁】
$name = '张三';
xdebug_debug_zval('name');
$test = &$name;
xdebug_debug_zval('name');
unset($test);
xdebug_debug_zval('name');

//结果如下:
name:
(refcount=1, is_ref=0)string '张三' (length=6)
name:
(refcount=2, is_ref=0)string '张三' (length=6)
name:
(refcount=1, is_ref=0)string '张三' (length=6)

【环行引用】php变量的回收机制只是简单的通过计数来处理(当refcount=0时,会回收内存),但这样会出现一个问题(老版本会导致内存泄漏)

$a = ['one']; //--- step0
xdebug_debug_zval('a');

//结果
 a:
(refcount=2, is_ref=0)
array (size=1)
  0 => (refcount=1, is_ref=0)string 'one' (length=3)

$a[] = &$a; // --- step1
xdebug_debug_zval('a');

//结果
a:
(refcount=2, is_ref=1)
array (size=2)
  0 => (refcount=1, is_ref=0)string 'one' (length=3)
  1 => (refcount=2, is_ref=1)
    &array<


unset($a);  //--- step2
xdebug_debug_zval('a');

//结果
a: no such symbol

  • step1执行unset之前,$a的refcount 为2,
  • 执行unset之后,$a的refcout为1
  • 因为是1不等于0,不能被回收内存,即为垃圾
  • 当然,在php脚本执行完毕后,所分配的内存将全部被回收(php-fpm模式)
  • 但是现在php除了应用于脚本以外,更多的地方用于写守护服务(swoole),可能很久(比如一年)才结束脚本,这期间例如上面的程序会产生内存溢出

注意:unset并不能释放内存,需要看zval的refcount是否为0(敲黑板)

潜在的内存变化

print_r(memory_get_usage());
for($i=0;$i<100;$i++)
{
    $a = "test".$i;
    $$a = "temp";
}
print_r(memory_get_usage());
for($i=0;$i<100;$i++)
{
    $a = "test".$i;
    unset($$a);
}
print_r(memory_get_usage());

结果:
2006640
2017392
2014224

内存没有全部回收回来!
对于php的核心结构Hashtable来说,由于未知性,定义的时候不可能一次性分配足够多的内存块。
所以初始化的时候只会分配一小块,等不够的时候在进行扩容,而Hashtable只扩容不减少,所以就出现了上述的情况:
1.当存入100个变量的时候,符号表不够用了就进行一次扩容
2.当unset的时候只释放了”为变量值分配内存”,而“为变量名分配内存”是在符号表的,符号表并没有缩小
3.所以没收回来的内存是被符号表占去了。

1.php的内存申请: php并非简单的向os申请内存,而是会申请一大块内存,把其中一部分分给申请者,这样当再有逻辑来申请内存的时候,就不需要向os申请了,避免了频繁调用。当内存不够的时候才会再次申请
2.php的内存释放:当释放内存的时候,php并非会把内存还给os,而是把内存轨道自己维护的空闲内存列表,以便重复利用

GC垃圾回收机制

基本准则:

  • 1.如果一个zval的refcount增加,那么此zval还在使用,不属于垃圾
  • 2.如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾
  • 3.如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾

php官方手册的配图


图解

A:为了避免每次变量的refcount减少的时候都调用GC的算法进行垃圾判断,此算法会先把所有前面准则3情况下的zval节点放入一个节点(root)缓冲区【root buffer】,并且将这些zval节点标记成紫色,同时算法必须确保每一个zval节点在缓冲区中之出现一次。当缓冲区被节点塞满的时候,GC才开始开始对缓冲区中的zval节点进行垃圾判断。

B:当缓冲区满了之后,算法以深度优先对每一个节点所包含的zval进行减1操作,为了确保不会对同一个zval的refcount重复执行减1操作,一旦zval的refcount减1之后会将zval标记成灰色。需要强调的是,这个步骤中,起初节点zval本身不做减1操作,但是如果节点zval中包含的zval又指向了节点zval(环形引用),那么这个时候需要对节点zval进行减1操作。

C:算法再次以深度优先判断每一个节点包含的zval的值,如果zval的refcount等于0,那么将其标记成白色(代表垃圾),如果zval的refcount大于0,那么将对此zval以及其包含的zval进行refcount加1操作,这个是对非垃圾的还原操作,同时将这些zval的颜色变成黑色(zval的默认颜色属性)

D:遍历zval节点,将C中标记成白色的节点zval释放掉。

相关函数

缓存区达到临界值时,遍历删除是垃圾的值

  • __destruct() 析构函数,是在垃圾对象被回收时执行。

  • unset 销毁的是指向对象的变量,而不是这个对象。

  • $a=null 将变量指向空容器

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353