PHP: 循环内有一些内存问题
PHP: Having some memory issues inside a loop
我有一个类似于以下内容的片段:
while (true) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
此代码段应 运行 作为守护程序服务,但我在完成这项工作时遇到了很多麻烦。
问题:每次迭代都会增加进程内存使用量。就好像在每次新的迭代中,一个新的 $myObject
被实例化,但前一个仍在内存中分配,等等。
我试过:
- 到
unset
循环结束时的所有变量(就在sleep()
之前)。
- 将所有变量设置为
null
。
- 将它们封装在一个单独的函数中 (
while (true) { doThis(); }
)
- 手动调用
gc_collect_cycles()
None 致力于减少内存使用。
我不知道如何强制释放所有内存。
很可能(根据给定的信息)仍然存在对已创建对象的引用,这会阻止垃圾收集器从内存中 removing the object。由于它基本上是对引用进行计数,因此可以通过复制值或小心地取消设置来确保没有存储任何引用。
通常情况下,使用 while(true) 结构时,不正是因为这个原因而创建对象并使其尽可能独立以确保不会实际发生内存泄漏会更容易。
我知道这个答案不是很有帮助(而且我没有足够的代表来评论这个问题)但它可能会让你走上正轨。
我正在将我之前的评论汇总到这里的答案中。这并没有准确解释如何释放分配的内存,但它会指导您通过一种方法来发现应用程序中的原因。这样,您就可以优化您的代码了。
查找内存使用瓶颈通常是一项具有挑战性的任务。您可以从查看 I/O-related 调用开始,例如数据库查询、文件访问甚至网络。除了增加执行时间之外,有时这些操作还可以分配一些内存。
如果您已经从内存中删除了 I/O 操作返回的资源,并且没有注意到分配的内存有明显减少,下一步可能是使用 Blackfire (https://blackfire.io/).
Blackfire 将为您提供每个函数调用的详细视图及其内存、CPU 和执行时间的统计信息。有了这些数据,就可以检查哪些操作分配了过多的内存。当您将鼠标指针放在呼叫详细信息中的内存条上时,您可以找到此信息,如下所示:
我最好的猜测(由于不了解所涉及的 类 的内部结构)是 类 将其他对象指定为它们的属性(或者它们可能具有自引用)或者所使用的数组(因为代码示例类似于真实情况)具有对自身的引用,如果数组的大小很大,这将解释内存泄漏。
如果有帮助,请查看php.net中的引用计数基础:
正如@m1lt0n 和@MartPluijmaekers 上面提到的,这可能是与对象引用相关的问题。
我们不知道你的 Class()
class 和 getSomeOtherObj()
方法里面有什么,所以我不能肯定地说什么,但是下面的代码片段可能会有所帮助你弄清楚是不是这样。
/* Here is your Class */
class Class {
public function __construct () {
$this->child = new AnotherClass( $this );
}
/* This is what you have to have ... */
protected function __destruct () {
unset( $this->child );
}
}
/* Here is the other class */
class AnotherClass {
public function __construct ( $parent ) {
$this->parent = $parent;
}
}
/* Infinite Loop */
while ( true ) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
$myArray = [1, 2, 3];
/* manually destroying the object */
$myObject->__destruct();
unset( $myObject );
/* rest of free-ing goes here ... */
sleep(1); //Sleep at the end, to save CPU.
}
该片段应该是不言自明的。
问题是你在一个无限循环中,请求没有结束。 PHP 的垃圾收集器设计为在请求结束时处理,否则用户无法访问。 PHP 旨在被调用和丢弃,而不是无限期地保持活动状态。因此它是不可修复的。因此,我建议创建一个 chron 作业,定期重新启动 php 循环,从而结束请求并释放内存。有关详细信息,请参阅 this document。
经过大量研究,我终于确信没有办法手动强制释放内存或强制销毁对象。
然而,帮助我降低内存使用率的东西(绝对防止无限内存堆叠是不可能的)是意识到没有循环范围在PHP 并且垃圾回收发生在切换范围时。
在 C# 或 Java 中,在 while (...) {}
中创建的变量只能从循环中访问。这不是 PHP 的标准。从 while
指令中创建的 $myObject
可在整个应用程序中访问。
这意味着提供的代码段可以更好地呈现为:
while (true) {
myFunc();
}
function myFunc()
{
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
将逻辑封装在一个函数中会强制范围发生变化,这意味着垃圾收集器将在每次迭代时被调用。这 没有 解决了我的问题,但它稍微降低了我的 RAM 使用率。
我从这次经历中学到的是 PHP 可能不适合这个特定的项目要求。我需要更多地控制内存,而 PHP 不提供对 created/destroyed 对象的任何控制。一些本机函数没有正确释放内存(特别是那些 I/O、数据库访问和 memcached)。
我有一个类似于以下内容的片段:
while (true) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
此代码段应 运行 作为守护程序服务,但我在完成这项工作时遇到了很多麻烦。
问题:每次迭代都会增加进程内存使用量。就好像在每次新的迭代中,一个新的 $myObject
被实例化,但前一个仍在内存中分配,等等。
我试过:
- 到
unset
循环结束时的所有变量(就在sleep()
之前)。 - 将所有变量设置为
null
。 - 将它们封装在一个单独的函数中 (
while (true) { doThis(); }
) - 手动调用
gc_collect_cycles()
None 致力于减少内存使用。
我不知道如何强制释放所有内存。
很可能(根据给定的信息)仍然存在对已创建对象的引用,这会阻止垃圾收集器从内存中 removing the object。由于它基本上是对引用进行计数,因此可以通过复制值或小心地取消设置来确保没有存储任何引用。
通常情况下,使用 while(true) 结构时,不正是因为这个原因而创建对象并使其尽可能独立以确保不会实际发生内存泄漏会更容易。
我知道这个答案不是很有帮助(而且我没有足够的代表来评论这个问题)但它可能会让你走上正轨。
我正在将我之前的评论汇总到这里的答案中。这并没有准确解释如何释放分配的内存,但它会指导您通过一种方法来发现应用程序中的原因。这样,您就可以优化您的代码了。
查找内存使用瓶颈通常是一项具有挑战性的任务。您可以从查看 I/O-related 调用开始,例如数据库查询、文件访问甚至网络。除了增加执行时间之外,有时这些操作还可以分配一些内存。
如果您已经从内存中删除了 I/O 操作返回的资源,并且没有注意到分配的内存有明显减少,下一步可能是使用 Blackfire (https://blackfire.io/).
Blackfire 将为您提供每个函数调用的详细视图及其内存、CPU 和执行时间的统计信息。有了这些数据,就可以检查哪些操作分配了过多的内存。当您将鼠标指针放在呼叫详细信息中的内存条上时,您可以找到此信息,如下所示:
我最好的猜测(由于不了解所涉及的 类 的内部结构)是 类 将其他对象指定为它们的属性(或者它们可能具有自引用)或者所使用的数组(因为代码示例类似于真实情况)具有对自身的引用,如果数组的大小很大,这将解释内存泄漏。
如果有帮助,请查看php.net中的引用计数基础:
正如@m1lt0n 和@MartPluijmaekers 上面提到的,这可能是与对象引用相关的问题。
我们不知道你的 Class()
class 和 getSomeOtherObj()
方法里面有什么,所以我不能肯定地说什么,但是下面的代码片段可能会有所帮助你弄清楚是不是这样。
/* Here is your Class */
class Class {
public function __construct () {
$this->child = new AnotherClass( $this );
}
/* This is what you have to have ... */
protected function __destruct () {
unset( $this->child );
}
}
/* Here is the other class */
class AnotherClass {
public function __construct ( $parent ) {
$this->parent = $parent;
}
}
/* Infinite Loop */
while ( true ) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
$myArray = [1, 2, 3];
/* manually destroying the object */
$myObject->__destruct();
unset( $myObject );
/* rest of free-ing goes here ... */
sleep(1); //Sleep at the end, to save CPU.
}
该片段应该是不言自明的。
问题是你在一个无限循环中,请求没有结束。 PHP 的垃圾收集器设计为在请求结束时处理,否则用户无法访问。 PHP 旨在被调用和丢弃,而不是无限期地保持活动状态。因此它是不可修复的。因此,我建议创建一个 chron 作业,定期重新启动 php 循环,从而结束请求并释放内存。有关详细信息,请参阅 this document。
经过大量研究,我终于确信没有办法手动强制释放内存或强制销毁对象。
然而,帮助我降低内存使用率的东西(绝对防止无限内存堆叠是不可能的)是意识到没有循环范围在PHP 并且垃圾回收发生在切换范围时。
在 C# 或 Java 中,在 while (...) {}
中创建的变量只能从循环中访问。这不是 PHP 的标准。从 while
指令中创建的 $myObject
可在整个应用程序中访问。
这意味着提供的代码段可以更好地呈现为:
while (true) {
myFunc();
}
function myFunc()
{
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
将逻辑封装在一个函数中会强制范围发生变化,这意味着垃圾收集器将在每次迭代时被调用。这 没有 解决了我的问题,但它稍微降低了我的 RAM 使用率。
我从这次经历中学到的是 PHP 可能不适合这个特定的项目要求。我需要更多地控制内存,而 PHP 不提供对 created/destroyed 对象的任何控制。一些本机函数没有正确释放内存(特别是那些 I/O、数据库访问和 memcached)。