首页 > 语言 > 关键词  > PHP5教程最新资讯  > 正文

浅谈PHP5中垃圾回收算法(Garbage Collection)的演化

2011-02-28 15:28 · 稿源:

前言:PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完全兼容。PHP5.3在PHP5.2的基础上做了诸多改进,其中垃圾回收算法就属于一个比较大的改变。本文将分别讨论PHP5.2和PHP5.3的垃圾回收机制,并讨论这种演化和改进对于程序员编写PHP的影响以及要注意的问题。

PHP变量及关联内存对象的内部表示

垃圾回收说到底是对变量及其所关联内存对象的操作,所以在讨论PHP的垃圾回收机制之前,先简要介绍PHP中变量及其内存对象的内部表示(其C源代码中的表示)。

PHP官方文档中将PHP中的变量划分为两类:标量类型和复杂类型。标量类型包括布尔型、整型、浮点型和字符串;复杂类型包括数组、对象和资源;还有一个NULL比较特殊,它不划分为任何类型,而是单独成为一类。

所有这些类型,在PHP内部统一用一个叫做zval的结构表示,在PHP源代码中这个结构名称为“_zval_struct”。zval的具体定义在PHP源代码的“Zend/zend.h”文件中,下面是相关代码的摘录。

 

typedef union _zvalue_value {
	long lval;					/* long value */
	double dval;				/* double value */
	struct {
		char *val;
		int len;
	} str;
	HashTable *ht;				/* hash table value */
	zend_object_value obj;
} zvalue_value;

struct _zval_struct {
	/* Variable information */
	zvalue_value value;		/* value */
	zend_uint refcount__gc;
	zend_uchar type;	/* active type */
	zend_uchar is_ref__gc;
};

其中联合体“_zvalue_value”用于表示PHP中所有变量的值,这里之所以使用union,是因为一个zval在一个时刻只能表示一种类型的变量。可以看到_zvalue_value中只有5个字段,但是PHP中算上NULL有8种数据类型,那么PHP内部是如何用5个字段表示8种类型呢?这算是PHP设计比较巧妙的一个地方,它通过复用字段达到了减少字段的目的。例如,在PHP内部布尔型、整型及资源(只要存储资源的标识符即可)都是通过lval字段存储的;dval用于存储浮点型;str存储字符串;ht存储数组(注意PHP中的数组其实是哈希表);而obj存储对象类型;如果所有字段全部置为0或NULL则表示PHP中的NULL,这样就达到了用5个字段存储8种类型的值。

而当前zval中的value(value的类型即是_zvalue_value)到底表示那种类型,则由“_zval_struct”中的type确定。_zval_struct即是zval在C语言中的具体实现,每个zval表示一个变量的内存对象。除了value和type,可以看到_zval_struct中还有两个字段refcount__gc和is_ref__gc,从其后缀就可以断定这两个家伙与垃圾回收有关。没错,PHP的垃圾回收全靠这俩字段了。其中refcount__gc表示当前有几个变量引用此zval,而is_ref__gc表示当前zval是否被按引用引用,这话听起来很拗口,这和PHP中zval的“Write-On-Copy”机制有关,由于这个话题不是本文重点,因此这里不再详述,读者只需记住refcount__gc这个字段的作用即可。

PHP5.2中的垃圾回收算法——Reference Counting

PHP5.2中使用的内存回收算法是大名鼎鼎的Reference Counting,这个算法中文翻译叫做“引用计数”,其思想非常直观和简洁:为每个内存对象分配一个计数器,当一个内存对象建立时计数器初始化为1(因此此时总是有一个变量引用此对象),以后每有一个新变量引用此内存对象,则计数器加1,而每当减少一个引用此内存对象的变量则计数器减1,当垃圾回收机制运作的时候,将所有计数器为0的内存对象销毁并回收其占用的内存。而PHP中内存对象就是zval,而计数器就是refcount__gc。

例如下面一段PHP代码演示了PHP5.2计数器的工作原理(计数器值通过xdebug.org得到):

<?php

$val1 = 100; //zval(val1).refcount_gc = 1;
$val2 = $val1; //zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(因为是Write on copy,当前val2与val1共同引用一个zval)
$val2 = 200; //zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此处val2新建了一个zval)
unset($val1); //zval(val1).refcount_gc = 0($val1引用的zval再也不可用,会被GC回收)

?>

Reference Counting简单直观,实现方便,但却存在一个致命的缺陷,就是容易造成内存泄露。很多朋友可能已经意识到了,如果存在循环引用,那么Reference Counting就可能导致内存泄露。例如下面的代码:

<?php

$a = array();
$a[] = & $a;
unset($a);

?>

这段代码首先建立了数组a,然后让a的第一个元素按引用指向a,这时a的zval的refcount就变为2,然后我们销毁变量a,此时a最初指向的zval的refcount为1,但是我们再也没有办法对其进行操作,因为其形成了一个循环自引用,如下图所示:

其中灰色部分表示已经不复存在。由于a之前指向的zval的refcount为1(被其HashTable的第一个元素引用),这个zval就不会被GC销毁,这部分内存就泄露了。

这里特别要指出的是,PHP是通过符号表(Symbol Table)存储变量符号的,全局有一个符号表,而每个复杂类型如数组或对象有自己的符号表,因此上面代码中,a和a[0]是两个符号,但是a储存在全局符号表中,而a[0]储存在数组本身的符号表中,且这里a和a[0]引用同一个zval(当然符号a后来被销毁了)。希望读者朋友注意分清符号(Symbol)的zval的关系。

在PHP只用于做动态页面脚本时,这种泄露也许不是很要紧,因为动态页面脚本的生命周期很短,PHP会保证当脚本执行完毕后,释放其所有资源。但是PHP发展到目前已经不仅仅用作动态页面脚本这么简单,如果将PHP用在生命周期较长的场景中,例如自动化测试脚本或deamon进程,那么经过多次循环后积累下来的内存泄露可能就会很严重。这并不是我在耸人听闻,我曾经实习过的一个公司就通过PHP写的deamon进程来与数据存储服务器交互。

由于Reference Counting的这个缺陷,PHP5.3改进了垃圾回收算法。

PHP5.3中的垃圾回收算法——Concurrent Cycle Collection in Reference Counted Systems

PHP5.3的垃圾回收算法仍然以引用计数为基础,但是不再是使用简单计数作为回收准则,而是使用了一种同步回收算法,这个算法由IBM的工程师在论文Concurrent Cycle Collection in Reference Counted Systems中提出。

这个算法可谓相当复杂,从论文29页的数量我想大家也能看出来,所以我不打算(也没有能力)完整论述此算法,有兴趣的朋友可以阅读上面的提到的论文(强烈推荐,这篇论文非常精彩)。

我在这里,只能大体描述一下此算法的基本思想。

首先PHP会分配一个固定大小的“根缓冲区”,这个缓冲区用于存放固定数量的zval,这个数量默认是10,000,如果需要修改则需要修改源代码Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES然后重新编译。

由上文我们可以知道,一个zval如果有引用,要么被全局符号表中的符号引用,要么被其它表示复杂类型的zval中的符号引用。因此在zval中存在一些可能根(root)。这里我们暂且不讨论PHP是如何发现这些可能根的,这是个很复杂的问题,总之PHP有办法发现这些可能根zval并将它们投入根缓冲区。

举报

  • 相关推荐
  • 最高网络安全标准,追觅扫地机获UL Solutions钻石级安全认证

    追觅X50系列扫地机器人获得UL Solutions颁发的"钻石级网络安全认证证书",成为全球首个获此认证的扫地机器人品牌。该认证表明产品在设备端防护、应用层数据加密、云端数据管理等全链路安全体系均达到国际最高标准。追觅还获得TÜV南德消费类物联网安全认证,构建起完善的安全防护体系。这些认证不仅验证了追觅产品的网络安全性能,更彰显了其在智能清洁赛道的核心竞争力。随着物联网安全要求提高,追觅构建的"全链路安全解决方案"或将成为行业重要参考,推动智能家居产业安全标准升级。

  • 技嘉科技 STEALTH Revolution 玩转简约装机美学 力邀创作者打造纯白梦幻桌机

    技嘉科技推出STEALTH Revolution活动,主打纯白美学装机概念。活动以X870AORUS STEALTH ICE主板为核心,邀请全球创作者打造个性化桌机。该系列采用背插式设计,简化装机流程,释放机箱空间,便于展示水冷设备和RGB灯效。新品涵盖X870/B850主板、RTX5090显卡及全景机箱等,联合10余家机箱品牌推出20余款兼容产品。活动提供限量主板供参赛者使用,并开放大众投票评选优秀作品,参与者有机会赢取丰厚奖品。技嘉旨在推动"背插主板×纯白机身"的装机新潮流,满足市场对简约风格的需求。

  • 王自如要回归老本行了:第一次正式播客讲苹果WWDC和Apple Intelligence

    王自如今天发文称,今晚会上线AI认知小宇宙”系列的最后一期视频,系列一共4期。 他表示,在在正式提笔前就有预感这个系列对于受众和流量的局限性,但依旧坚持把它放在我的频道中最优先需要创作的内容。 王自如称:如果把我的频道内容理解为一部电影,那这个系列就是电影的前传,它最核心的使命是在主线剧情出来前构建宏观的故事背景,是让我和我的用户之间拉�

  • 出门问问发布Agentic AI软硬结合产品TicNote,定义新一代“AI思考伙伴”

    出门问问发布新一代AI硬件TicNote,内置"Shadow AI"系统,实现"有记忆的AI记录+主动洞察+主动分析+陪伴创作"功能。该产品通过软硬件结合,成为用户的随身AI思考伙伴,适用于会议、商务沟通、学习等场景。TicNote具备20小时续航、10米远距收音、120+语言转写能力,支持跨文件项目管理。CEO李志飞表示,TicNote是"用AI的AI做AI"的AGI实践产品,标志着公司在软硬结合道路上更进一步。国内版已上线,海外版自2025年4月推出以来获广泛认可。

  • 微星发布AMD首款纯白ITX主板MPG B850I EDGE TI WIFI刀锋 钛

    微星推出全新MPG B850I EDGE TI WIFI刀锋钛主板,采用ITX规格设计,支持AMD锐龙9000系列处理器和AM5平台。主板配备10层服务器级PCB,提供稳定供电和超频性能。搭载5G有线网口和满血版WIFI7技术,实现低延迟高速传输。采用三层散热装甲设计,包括扩展型VRM散热片、M.2冰霜铠甲和主动散热风扇,确保高性能硬件稳定运行。主板首发售价1799元,提供3年质保+1年上门服务。

  • DigitalOcean 携手 AMD 推出 AMD Instinct MI300X GPU Droplet,加速 AI 创新

    DigitalOcean与AMD达成合作,将推出搭载AMD Instinct MI300X GPU的云服务器,支持AI/ML/HPC工作负载。该GPU具备192GB HBM3内存,能完整加载数十亿参数模型,显著提升训练和推理效率。服务定价每小时1.99美元起,支持1-8个GPU配置,并与Kubernetes无缝集成。未来还将推出MI325X GPU,并面向中国市场提供服务。此次合作旨在为开发者提供经济高效的AI开发解决方案,简化云端GPU部署流程。

  • vivo X Fold5支持连接Apple Watch 安卓史上第一次

    今日,vivo X Fold5正式发布,目标是最强生态破壁机”。 vivo X Fold5支持连接Apple Watch,这也是安卓手机史上第一次,可接听来电、查看信息,甚至同步健康数据到vivo健康应用,同时支持AirPods原生级体验。 此外,X Fold5还可在文件管理访问Mac,查看电脑文件,同时支持跨应用拖拽发送文件,X Fold5还可作为Mac的扩展屏。

  • 在版权诉讼中,Anthropic 赢下了关键AI训练裁决

    就像任何渴望成为作家的读者一样,Anthropic的语言模型并非为了抄袭或取代原作,而是为了创造出完全不同的内容……

  • Ceph存储效能飙升20%+!忆联PCIe5.0 ESSD智能多流技术攻克“隐形损耗”

    本文介绍了忆联Ceph分布式存储方案的技术优势。该方案基于PCIe5.0 ESSD,数据吞吐效率达上一代2倍;采用智能多流技术,性能优于主流竞品3.15%;有效抑制写放大效应(WAF),优于竞品20%-32%,可延长SSD寿命20%以上。方案通过硬件层面的创新突破,为云原生环境提供兼具超低延迟与超高一致性的存储基础设施,满足企业级客户对存储系统全生命周期稳定性的严苛需求。测试数据显示,在3副本配置下,QD32时随机读IOPS达175.5万,延迟稳定在1.1毫秒内;混合读写IOPS超50万,延迟约20毫秒。智能多流技术实现1.78超低WAF值,显著提升存储效能,是构建新一代分布式存储系统的理想选择。

  • iPad为何不运行macOS 苹果高管:macOS未对触摸屏做优化

    iPadOS 26带来了全新的应用视窗、下滑式菜单栏等功能,让iPad的操作体验更接近Mac,但苹果始终未让iPad运行macOS系统,其背后原因正式揭开。 苹果公司软件工程负责人Craig Federighi接受采访时称,iPadOS 26的新特性在生产力与简洁性之间取得了很好的平衡,macOS系统从未对触控屏做优化,我们要保留iPad最本质的简洁性,如果强行移植macOS,iPad将丧失其触控设备的独特价值。 Craig Fed