PHP 8.5 垃圾回收改进

PHP 8.5 垃圾回收改进

PHP 内存消耗的大幅优化已经很罕见了。近年来的内存改进范围较小,主要集中在某些类型变量的细节上。

比如改进垃圾回收器(GC)在边缘情况下的表现。Iljia Tovilo 为 PHP 8.5 贡献了这个优化,标题是"将枚举和静态伪闭包标记为不可回收"。

简单来说,对于这两种变量类型,循环回收器不会尝试"收集"它们,大量使用这些实例时就能避免不必要的 GC 运行。

这没有任何副作用,因为这些类型本身不可能形成循环引用,本来就不在 GC 的管辖范围内。

原文链接 PHP 8.5 垃圾回收改进

快速回顾:PHP 的垃圾回收器是如何工作的?

要理解为什么这很有用,先快速回顾一下 PHP 垃圾回收的工作原理。

简单来说,PHP 的 GC 不是持续运行的,而是由阈值触发。这个阈值是:内存中至少有 10,000 个可能存在循环引用的对象或数组。如果这些实例数量不断增加而无法清理,GC 可能会在越来越多的对象上运行,消耗宝贵的计算资源。

因此,减少符合垃圾回收条件的对象数量可以降低 GC 运行的概率,从而提升性能。

PHP 7.3 添加了一个实用函数 gc_status()文档),可以很方便地了解垃圾回收的性能情况。

实际收益是什么?

看一下 PR 中的例子,大量 first class callable 实例被添加到数组中,然后进行迭代。

php 复制代码
<?php

function foo() {}

$array = [];
for ($i = 0; $i < 10_000_000; $i++) {
    $array[] = foo(...);
}

//
// 这个循环是为了确保触发循环回收器:
// - `as $foo` 会增加数组元素的引用计数
// - 变量超出作用域后引用计数又会减少
//
foreach ($array as $foo) {}

echo json_encode(gc_status(), JSON_PRETTY_PRINT);

在旧版 PHP 中,这会触发循环回收器检查所有这些实例是否存在循环引用。由于这些实例不可能有循环引用,这些检查是多余的。对于 1000 万个条目,这导致了 44 次 GC 运行,而 PHP 8.5 及以后版本在相同代码下是 0 次。

定义:"静态伪闭包"

PR 标题说优化适用于"枚举和静态伪闭包"。静态伪闭包显然是更常见的情况,而且术语与用户层面的命名不一致,我们来深入了解一下:什么是"静态伪闭包"?

上面的例子使用了"静态伪闭包",在用户层面通常称为"first-class callable"。在这个例子中,就是 foo(...)

要判断一个 callable 是否是"伪闭包",可以检查该 callable 的反射类的 anonymous 标志是否返回 false,即 (new \ReflectionFunction($callable))->isAnonymous();

类型中的"静态"部分指的是 OOP 中的 static:没有访问对象实例(即 $this)的权限。

在下面这个不同闭包的列表中,你可以看到哪些被 PR 认为是静态伪闭包,哪些不是:

php 复制代码
<?php

class Foo 
{
    public static function bar(): void {}
    public function baz(): void {}    
}

function bar(): void {}

//
// 静态伪闭包
//
var_dump(...);
bar(...);
Foo::bar(...);
$callable = Closure::fromCallable('bar');
$callable(...);

// 伪闭包,但非静态
(new Foo())->baz(...);
// 匿名函数,不是伪闭包
$anon = static function () {};
$anon(...);

枚举的情况

第二种被优化的类型是枚举实例。这在实际项目中的影响可能较小,因为只有当代码库包含大量枚举且每个枚举有很多 case 时才会影响性能。枚举实例是单例的,每个枚举 case 只会被实例化一次。即使有数千个引用指向相同的三四个枚举 case,也不会有影响,因为每个枚举 case 在 GC 的根缓冲区中只能出现一次。

总结

这样的小改进表明,PHP 的内存管理在已经相当成熟的情况下仍然可以继续优化。

这个改动可能在某些情况下提升性能,但更重要的是,它确保垃圾回收运行不会在无关的检查上浪费时间。

感谢 Iljia Tovilo 的贡献。

相关推荐
m0_748229991 小时前
Laravel4.x核心更新全解析
开发语言·php
h7ml2 小时前
电商返利系统中佣金计算的幂等性保障与对账补偿机制实现
服务器·前端·php
北辰当尹3 小时前
【小迪安全2023】day43 php应用&SQL注入&符号拼接&请求方法&HTTP头&JSON&编码类
sql·安全·php
csdn_aspnet3 小时前
PHP常用算法解析:从Web开发到性能优化的实战
php
嵌入式×边缘AI:打怪升级日志3 小时前
Libmodbus 源码总体分析:框架、数据结构与核心函数详解
开发语言·数据结构·php
Remember_9933 小时前
Spring 中 REST API 调用工具对比:RestTemplate vs OpenFeign
java·网络·后端·算法·spring·php
北辰当尹18 小时前
【小迪安全2023】day42 php应用&mysql架构&sql注入&跨库查询&文件读写&权限操作
mysql·安全·php
2501_9481201520 小时前
基于机器学习的网络异常检测与响应技术研究
网络·机器学习·php
郑州光合科技余经理21 小时前
技术架构:海外版外卖平台搭建全攻略
java·大数据·人工智能·后端·小程序·架构·php
2301_7679026421 小时前
第 5 章 docker网络
网络·docker·php