PHP 闭包和生成器可以保存循环引用
PHP中的循环引用是内存泄漏的常见原因。当对象直接或间接地相互引用时,就会发生循环引用。幸运的是,PHP有一个垃圾收集器可以检测和清理循环引用。但是,这会消耗CPU周期并可能减慢应用程序的速度。
垃圾收集器会在内存中存在10,000个可能的循环对象或数组,并且其中一个超出作用域时触发。
如果您有少量使用大量内存的对象,则永远不会触发垃圾收集。即使内存被垃圾收集器应该收集的孤立对象使用,您也可能达到内存限制。
这就是为什么您应该识别创建循环引用的情况并避免它们的原因。
理想情况下,对于Web应用程序,您希望禁用垃圾收集器,并在发送响应后让PHP释放所有内存。但这对于长时间运行的脚本(例如守护进程或工作进程)来说是危险的,因为内存泄漏会随着时间的推移而累积,并通过频繁调用垃圾收集器来减慢应用程序的速度。
在本文中,我们将探讨闭包和生成器如何保存循环引用以及如何防止它们。
- 关于循环引用
- 循环引用的典型示例
- 使用弱引用防止循环引用
- 闭包和循环引用
- 生成器和循环引用
- 结论
关于循环引用
循环引用的典型示例
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { public function __construct(public A $a) {} }
在此示例中,A和B相互引用。当您创建A的实例时,它会创建一个引用A的B实例。这会创建一个循环引用。
为了检测循环引用,我们可以使用gc_collect_cycles()
手动触发垃圾收集器,并使用gc_status()
读取收集到的引用的数量。
// 创建的对象但未分配给变量 new A(); gc_collect_cycles(); print_r(gc_status());
这将输出:
<code>Array ( ... [collected] => 2 ... )</code>
此示例表明垃圾收集器已检测到并删除了2个具有循环引用的对象。
您还可以使用xdebug_debug_zval()
函数查看对象的引用数量。
使用弱引用防止循环引用
当遇到循环引用时,一个简单的解决方案是使用弱引用。弱引用是一个对象,它持有的引用不会阻止垃圾收集器收集它引用的对象。在PHP中,您可以使用WeakReference
类创建弱引用。
这需要对代码进行一些更改。B类现在存储WeakReference
对象而不是A对象。您必须使用WeakReference
对象的get()
方法访问A对象。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { /** @var WeakReference<a> $a */ public WeakReference $a; public function __construct(A $a) { $this->a = WeakReference::create($a); } }
// 创建的对象但未分配给变量 new A(); gc_collect_cycles(); print_r(gc_status()); // [collected] => 0
在输出中,您将看到收集到的引用数量现在为0。
提示1:仅在必要时使用弱引用来防止循环引用。
闭包和循环引用
PHP中闭包的概念是创建一个可以访问父作用域中变量的函数。如果您不小心,这可能会导致循环引用。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { public function __construct(public A $a) {} }
在此示例中,闭包$a->b
引用父作用域中的变量$a
。循环引用很容易发现,因为引用是明确的。
但是,如果您使用闭包的简写语法,则可能会以更隐蔽的方式出现相同的问题。使用箭头函数,变量$a
不会在闭包中显式引用,但它仍然被按引用捕获。
// 创建的对象但未分配给变量 new A(); gc_collect_cycles(); print_r(gc_status());
在此示例中,收集到的引用数量为2,表明存在循环引用。
闭包中对$this的引用
在类方法中创建的任何非静态闭包都将对对象实例($this
)具有引用,即使没有访问$this
也是如此。
<code>Array ( ... [collected] => 2 ... )</code>
这是因为$this
引用始终在闭包中按引用捕获。可以使用Reflection::getClosureThis()
访问它。
class A { public B $b; public function __construct() { $this->b = new B($this); } } class B { /** @var WeakReference<a> $a */ public WeakReference $a; public function __construct(A $a) { $this->a = WeakReference::create($a); } }
如果从全局作用域或静态方法中创建闭包,则$this
引用为null。
提示2:如果您不需要
$this
,则始终使用static function () {}
或static fn () =>
来创建闭包。
生成器和循环引用
我们来说说这篇文章的原因。我最近发现了一些东西: 生成器会保留引用,只要它们没有被耗尽。
在此示例中,该类将生成器存储在一个属性中,但生成器对对象实例具有$this
引用。
生成器表现得像一个闭包,并保留对对象实例的引用。
// 创建的对象但未分配给变量 new A(); gc_collect_cycles(); print_r(gc_status()); // [collected] => 0
类实例被垃圾收集器收集,因为它对生成器有引用,而生成器对对象实例有引用。
一旦生成器被耗尽,引用就会被释放,对象实例就会从内存中删除。
function createCircularReference() { $a = new stdClass(); $a->b = function () use ($a) { return $a; }; return $a; }
提示3:通过迭代始终耗尽生成器。
提示4:使用静态方法或闭包来创建生成器,以免保留对对象实例的引用。
结论
循环引用是PHP中内存泄漏的常见原因。即使垃圾收集器可以检测和清理循环引用,它也会消耗CPU周期并可能减慢应用程序的速度。您必须检测创建此类循环引用的情况并调整代码以防止它们。使用弱引用可以防止循环引用,但一些简单的技巧可以帮助您首先防止循环引用:
- 如果不需要
$this
,则使用static function () {}
或static fn () =>
来创建闭包。 - 通过迭代始终耗尽生成器。
- 使用静态方法或闭包来创建生成器,以免保留对对象实例的引用。
阅读更多
- PHP垃圾收集——性能注意事项
- PHP中的垃圾收集是什么?如何充分利用它?
- memprof——PHP的内存分析器。帮助查找PHP脚本中的内存泄漏。
- Xdebug的内置分析器
以上是PHP 闭包和生成器可以保存循环引用的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

在PHP中,应使用password_hash和password_verify函数实现安全的密码哈希处理,不应使用MD5或SHA1。1)password_hash生成包含盐值的哈希,增强安全性。2)password_verify验证密码,通过比较哈希值确保安全。3)MD5和SHA1易受攻击且缺乏盐值,不适合现代密码安全。

PHP类型提示提升代码质量和可读性。1)标量类型提示:自PHP7.0起,允许在函数参数中指定基本数据类型,如int、float等。2)返回类型提示:确保函数返回值类型的一致性。3)联合类型提示:自PHP8.0起,允许在函数参数或返回值中指定多个类型。4)可空类型提示:允许包含null值,处理可能返回空值的函数。

PHP主要是过程式编程,但也支持面向对象编程(OOP);Python支持多种范式,包括OOP、函数式和过程式编程。PHP适合web开发,Python适用于多种应用,如数据分析和机器学习。

PHP适合网页开发和快速原型开发,Python适用于数据科学和机器学习。1.PHP用于动态网页开发,语法简单,适合快速开发。2.Python语法简洁,适用于多领域,库生态系统强大。

PHP起源于1994年,由RasmusLerdorf开发,最初用于跟踪网站访问者,逐渐演变为服务器端脚本语言,广泛应用于网页开发。Python由GuidovanRossum于1980年代末开发,1991年首次发布,强调代码可读性和简洁性,适用于科学计算、数据分析等领域。

PHP的核心优势包括易于学习、强大的web开发支持、丰富的库和框架、高性能和可扩展性、跨平台兼容性以及成本效益高。1)易于学习和使用,适合初学者;2)与web服务器集成好,支持多种数据库;3)拥有如Laravel等强大框架;4)通过优化可实现高性能;5)支持多种操作系统;6)开源,降低开发成本。

PHP在现代化进程中仍然重要,因为它支持大量网站和应用,并通过框架适应开发需求。1.PHP7提升了性能并引入了新功能。2.现代框架如Laravel、Symfony和CodeIgniter简化开发,提高代码质量。3.性能优化和最佳实践进一步提升应用效率。

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip
