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中文网其他相关文章!

Laravel使用其直观的闪存方法简化了处理临时会话数据。这非常适合在您的应用程序中显示简短的消息,警报或通知。 默认情况下,数据仅针对后续请求: $请求 -

PHP客户端URL(curl)扩展是开发人员的强大工具,可以与远程服务器和REST API无缝交互。通过利用Libcurl(备受尊敬的多协议文件传输库),PHP curl促进了有效的执行

Laravel 提供简洁的 HTTP 响应模拟语法,简化了 HTTP 交互测试。这种方法显着减少了代码冗余,同时使您的测试模拟更直观。 基本实现提供了多种响应类型快捷方式: use Illuminate\Support\Facades\Http; Http::fake([ 'google.com' => 'Hello World', 'github.com' => ['foo' => 'bar'], 'forge.laravel.com' =>

PHP日志记录对于监视和调试Web应用程序以及捕获关键事件,错误和运行时行为至关重要。它为系统性能提供了宝贵的见解,有助于识别问题并支持更快的故障排除

您是否想为客户最紧迫的问题提供实时的即时解决方案? 实时聊天使您可以与客户进行实时对话,并立即解决他们的问题。它允许您为您的自定义提供更快的服务

文章讨论了PHP 5.3中引入的PHP中的晚期静态结合(LSB),从而允许静态方法的运行时分辨率调用以获得更灵活的继承。 LSB的实用应用和潜在的触摸


热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

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

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

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。