浅谈PHP 5中垃圾回收算法的演化

本文将分别讨论PHP5.2和PHP5.3的垃圾回收机制,并讨论这种演化和改进对于程序员编写PHP的影响以及要注意的问题。

  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”文件中,下面是相关代码的摘录。

复制代码
 1 typedef union _zvalue_value {  
 2     long lval;                  /* long value */ 
 3     double dval;                /* double value */ 
 4     struct {  
 5         char *val;  
 6         int len;  
 7     } str;  
 8     HashTable *ht;              /* hash table value */ 
 9     zend_object_value obj;  
10 } zvalue_value;  
11  
12 struct _zval_struct {  
13     /* Variable information */ 
14     zvalue_value value;       
15 /* value */ 
16     zend_uint refcount__gc;  
17     zend_uchar type;    /* active type */ 
18     zend_uchar is_ref__gc;  
19 }; 
复制代码

 

  其中联合体“_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得到):

  

复制代码
1 <?php  
2  
3 $val1 = 100; //zval(val1).refcount_gc = 1;  
4 $val2 = $val1; //zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(因为是Write on copy,当前val2与val1共同引用一个zval)  
5 $val2 = 200; //zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此处val2新建了一个zval)  
6 unset($val1); //zval(val1).refcount_gc = 0($val1引用的zval再也不可用,会被GC回收)  
7  
8 ?> 
复制代码

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

  

复制代码
1 <?php  
2  
3 $a = array();  
4 $a[] = & $a;  
5 unset($a);  
6  
7 ?> 
复制代码

 

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

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

  其中灰色部分表示已经不复存在。由于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并将它们投入根缓冲区。

当根缓冲区满额时,PHP就会执行垃圾回收,此回收算法如下:

1、对每个根缓冲区中的根zval按照深度优先遍历算法遍历所有能遍历到的zval,并将每个zval的refcount减1,同时为了避免对同一zval多次减1(因为可能不同的根能遍历到同一个zval),每次对某个zval减1后就对其标记为“已减”。

2、再次对每个缓冲区中的根zval深度优先遍历,如果某个zval的refcount不为0,则对其加1,否则保持其为0。

3、清空根缓冲区中的所有根(注意是把这些zval从缓冲区中清除而不是销毁它们),然后销毁所有refcount为0的zval,并收回其内存。

如果不能完全理解也没有关系,只需记住PHP5.3的垃圾回收算法有以下几点特性:

1、并不是每次refcount减少时都进入回收周期,只有根缓冲区满额后在开始垃圾回收。

2、可以解决循环引用问题。

3、可以总将内存泄露保持在一个阈值以下。

  PHP5.2与PHP5.3垃圾回收算法的性能比较

  由于我目前条件所限,我就不重新设计试验了,而是直接引用PHP Manual中的实验,关于两者的性能比较请参考PHP Manual中的相关章节:http://www.php.net/manual/en/features.gc.performance-considerations.php。

首先是内存泄露试验,下面直接引用PHP Manual中的实验代码和试验结果图:

 

 

PHP

可以看到在可能引发累积性内存泄露的场景下,PHP5.2发生持续累积性内存泄露,而PHP5.3则总能将内存泄露控制在一个阈值以下(与根缓冲区大小有关)。

  另外是关于性能方面的对比:

  

复制代码
 1 <?php  
 2 class Foo  
 3 {  
 4     public $var = '3.1415962654';  
 5 }  
 6  
 7 for ( $i = 0; $i <= 1000000; $i++ )  
 8 {  
 9     $a = new Foo;  
10     $a->self = $a;  
11 }  
12  
13 echo memory_get_peak_usage(), "\n";  
14 ?> 
复制代码

这个脚本执行1000000次循环,使得延迟时间足够进行对比。

然后使用CLI方式分别在打开内存回收和关闭内存回收的的情况下运行此脚本:

  

1 time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php  
2 # and 
3 time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php 

  

在我的机器环境下,运行时间分别为6.4s和7.2s,可以看到PHP5.3的垃圾回收机制会慢一些,但是影响并不大。

  与垃圾回收算法相关的PHP配置

可以通过修改php.ini中的zend.enable_gc来打开或关闭PHP的垃圾回收机制,也可以通过调用gc_enable()或gc_disable()打开或关闭PHP的垃圾回收机制。在PHP5.3中即使关闭了垃圾回收机制,PHP仍然会记录可能根到根缓冲区,只是当根缓冲区满额时,PHP不会自动运行垃圾回收,当然,任何时候您都可以通过手工调用gc_collect_cycles()函数强制执行内存回收。

转载于:https://www.cnblogs.com/haohaoyuan/p/9115408.html

虎符+红明谷+ctfshow渔人杯赛后复现 目录标题虎符CTFWeb-签到MISC-你会日志分析吗红明谷CTFWeb-write_shellMISC-我的心是冰冰的ctfshow渔人杯签到抽奖感受下气氛神仙姐姐阿拉丁迷飘啊飘简单二维码我跟你拼了 虎符CTF Web-签到 参考详细分析PHP源代码后门事件及其供应链安全启示 根据文章提示,添加恶意请求头 User-Agentt: zerodiumphpinfo(); 可以看到phpinfo内容 继续构造,查看根目录下的文件 User-Agentt: zerodiumsystem('ls /'); 阅读详情

相关推荐

3D Slicer:从入门到精通(二)——入门指南

本篇文章包含开始使用3D Slicer所需的信息,包括如何安装和快速开始使用基本功能,以及在哪里找到更多信息。

walt3433的博客 2万+

【转】浅谈PHP 5中垃圾回收算法的演化

PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完全兼容。PH

玲珑巫女的学习笔记 1004

别再为跨页读写发愁了!STM32F407模拟I2C驱动24C256/512的避坑指南

本文详细解析了STM32F407模拟I2C驱动24C256/512的跨页写入问题,揭示了24C系列存储器'写满一页不会自动翻页'的关键机制,并提供了经过验证的分页写入算法和优化方案。通过GPIO时序优化、地址计算和错误处理等技巧,帮助开发者解决数据错乱、覆盖等常见问题,实现稳定可靠的EEPROM读写操作。

weixin_42631411的博客 377

使用swoole要学习的知识点

这边只是列一个大纲总结,想深度学习还是自己要去深挖和debug学习。基础编程知识多进程编程基础的TCP/UDP认知协程目录基础编程知识阻塞和非阻塞的区别垃圾回收(Garbage Collection,简称 GC)面向对象编程资源及连接句柄资源(Resource)连接句柄(Handle)资源管理性能考虑多进程编程php-fpm和swoole的多进程模型及其区别进程间通讯和进程隔离,进程信号量进程间通信(IPC)进程隔离进程信号量(Semaphores)基础的TCP/UDP认知TCP和UDP的区别客户端和服务端

vbgesab的博客 1371

快充协议全兼容!HUSB390对比测试:iPhone/小米/华为哪个充电最快?

本文深入评测了HUSB390快充协议芯片的全兼容性能,展示了其在iPhone、小米和华为设备上的极速充电表现。通过智能功率分配算法和多协议并行处理引擎,HUSB390实现了高效的动态调压和温度控制,显著提升多设备充电效率。特别适合需要同时为不同品牌设备快充的用户。

dd012的博客 1万+

【WILEY】从Revision到Online:一次投稿的完整状态演进与关键节点实战解析

本文详细解析了从Revision到Online发表的完整投稿流程,包括修改稿准备、回复信撰写、状态跟踪等关键节点。特别针对Wiley期刊,提供了实用的操作技巧和常见问题解决方案,帮助作者高效完成论文修改与发表。

weixin_29219189的博客 361

PHP 的垃圾回收机制(GC)

PHP进阶学习之垃圾回收机制详解PHP ZVAL是什么意思?PHP 垃圾回收机制详解程序在创建对象或者数组等引用类型实体的时候,系统会在堆内存上为之分配一段内存区,用来保存这些对象,当这些对象永久地失去引用后,就会变成垃圾,等待系统垃圾回收机制进行回收。垃圾回收机制(GC)是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。

weixin_43844718的博客 2463

RTK_ROS_导航(1): GNSS里程计

1. RTK 配置 4G CORS + 4G 网络 + 户外有信号,不能实现RTK,就恢复出厂设置 输出信息包含(一般需要三个同时打开): GAPPA:包含位置信息 GPVTG:包含速度信息 GPHDT:包含定向的朝向信息 2. ROS驱动 驱动安装sudo apt install ros-noetic-nmea-navsat-driver 运行,需要修改该功能包的启动端口,会自动解析上面的3个RTK语句信息roslaunch nmea_navsat_driver nmea_serial_d

旋风小短腿 4265

PHP的垃圾回收机制(全网详解)

当一个PHP对象不再被引用时,它就成为垃圾。垃圾回收器会定期扫描内存中的所有对象,将没有引用的对象标记为垃圾,并释放它们占用的内存空间,以便其他对象可以使用这些空间。为了避免这种情况的发生,PHP提供了一种手动解除引用的方法,即将对象赋值为null,这样就可以让对象的引用计数器降为0,从而被垃圾回收器释放。然而,在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。

MrWangisgoodboy的博客 3083

主成分分析(Principal Component Analysis,PCA)

主成分分析1.主成分分析1.1 降维(Dimensionality Reduction)1.2 均值(Mean)1.3 方差(Variance)1.4 协方差(Covariance)1.5 协方差矩阵(Covariance Matrix)1.6 线性变换(Linear Transformation)1.7 主成分分析(PCA)1.8 PCA和SVD的关系 1.主成分分析 关于PCA的介绍:用最直观的方式告诉你:什么是主成分分析PCA 笔记来源:Principal Component Analysis (PC

一个搬运知识的笨小孩 3230

权重衰减weight_decay参数从入门到精通

Weight Decay是一个正则化技术,作用是**抑制模型的过拟合**,以此来提高模型的泛化性。 目前网上对于Weight Decay的讲解都比较泛,都是短短的几句话,但对于其原理、实现方式大多就没有讲解清楚,本文将会逐步解释weight decay机制。

iioSnail的博客 4万+

PHP 垃圾回收机制(GC)

PHP 垃圾回收机制(GC)和例题。

Aiwin的博客 3917

YOLOv11与CLIP融合的零样本目标检测技术:原理、实现与创新实践

YOLO(You Only Look Once)系列作为单阶段检测器的代表,以其实时高效的特性在工业界获得广泛应用。YOLOv11作为该系列的最新演进,引入了多项架构创新: C3K2模块:采用双小卷积核替代传统大卷积核,在保持感受野的同时减少计算量。当参数c3k=False时退化为YOLOv8的C2F模块,提供灵活的精度-效率权衡。 C2PSA(Convolutional block with Parallel Spatial Attention):通过并行空间注意力机制增强对微小目标的特征提取能力,这对

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1068

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

前言:PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不...

153

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

php5和7垃圾回收的区别,浅谈PHP5中垃圾回收算法(Garbage Collection)的演化

当根缓冲区满额时,PHP就会执行垃圾回收,此回收算法如下:1、对每个根缓冲区中的根zval按照深度优先遍历算法遍历所有能遍历到的zval,并将每个zval的refcount减1,同时为了避免对同一zval多次减1(因为可能不同的根能遍历到同一个zval),每次对某个zval减1后就对其标记为“已减”。2、再次对每个缓冲区中的根zval深度优先遍历,如果某个zval的refcount不为0,则对其加...

weixin_36009977的博客 138

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

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

浅谈PHP5中垃圾回收算法(Garbage Collection)的演化 2011-02-27 00:10 by T2噬菌体, 5030 阅读, 10 评论, 收藏, 编辑 前言 PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在

walle的博客 881

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

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

【转】浅谈PHP5中垃圾回收算法(Garbage Collection)的演化 前言 PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目...

weixin_34405925的博客 111

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

前言 PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完全兼...

weixin_34357887的博客 131

php垃圾回收机制

浅谈PHP5中垃圾回收算法(Garbage Collection)的演化 PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5....

猛犸象 137

php垃圾回收,PHP垃圾回收机制的理解

PHP垃圾回收机制是php5之后才有的这个东西,下面我来给大家介绍一下关于PHP垃圾回收机制一些理解,希望对各位同学有所帮助。php 5.3之前使用的垃圾回收机制是单纯的“引用计数”,也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器 1;当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。“引用计数”存在问题,就是当两个或多个...

weixin_35316606的博客 201
上一篇: 在php中调用接口以及编写接口
下一篇: Cookie详解整理
dichen3237
博客等级 码龄11年 0粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值