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

phpientifiesauser'ssessionusessessionSessionCookiesAndSessionIds.1)whiwSession_start()被称为,phpgeneratesainiquesesesessionIdStoredInacookInAcookInamedInAcienamedphpsessidontheuser'sbrowser'sbrowser.2)thisIdAllowSphptptpptpptpptpptortoreTessessionDataAfromtheserverMtheserver。

PHP会话的安全可以通过以下措施实现:1.使用session_regenerate_id()在用户登录或重要操作时重新生成会话ID。2.通过HTTPS协议加密传输会话ID。3.使用session_save_path()指定安全目录存储会话数据,并正确设置权限。

phpsessionFilesArestoredIntheDirectorySpecifiedBysession.save_path,通常是/tmponunix-likesystemsorc:\ windows \ windows \ temponwindows.tocustomizethis:tocustomizEthis:1)useession_save_save_save_path_path()

ToretrievedatafromaPHPsession,startthesessionwithsession_start()andaccessvariablesinthe$_SESSIONarray.Forexample:1)Startthesession:session_start().2)Retrievedata:$username=$_SESSION['username'];echo"Welcome,".$username;.Sessionsareserver-si

利用会话构建高效购物车系统的步骤包括:1)理解会话的定义与作用,会话是服务器端的存储机制,用于跨请求维护用户状态;2)实现基本的会话管理,如添加商品到购物车;3)扩展到高级用法,支持商品数量管理和删除;4)优化性能和安全性,通过持久化会话数据和使用安全的会话标识符。

本文讨论了PHP中的crypt()和password_hash()之间的差异,以进行密码哈希,重点介绍其实施,安全性和对现代Web应用程序的适用性。

文章讨论了通过输入验证,输出编码以及使用OWASP ESAPI和HTML净化器之类的工具来防止PHP中的跨站点脚本(XSS)。


热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

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

热门文章

热工具

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

Dreamweaver CS6
视觉化网页开发工具

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

Atom编辑器mac版下载
最流行的的开源编辑器

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器