在PHP程序中,我按顺序读取一堆文件(file_get_contents
),gzdecode
,json_decode
结果,分析内容,将大部分内容丢弃,以及在阵列中存储大约1%。
不幸的是,随着每次迭代(我遍历包含文件名的数组),似乎有一些内存丢失(根据memory_get_peak_usage
,每次约2-10 MB)。我对我的代码进行了双重和三重检查;我没有在循环中存储不需要的数据(并且所需的数据总体上不超过大约10MB),但我经常重写(实际上,数组中的字符串)。显然,PHP没有正确释放内存,因此使用越来越多的RAM直到它达到极限。
有没有办法强制垃圾收集?或者,至少,找出内存的使用位置?
答案 0 :(得分:37)
它与内存碎片有关。
考虑两个字符串,连接到一个字符串。每个原件必须保留,直到创建输出。输出比任一输入都长
因此,必须进行新的分配以存储这种串联的结果。原始字符串被释放但它们是小块的内存
在'str1' . 'str2' . 'str3' . 'str4'
的情况下,每个都会创建几个临时值。 - 并且没有一个适合被释放的空间。由于存储器的其他用途,字符串可能不会在连续的内存中布局(即,每个字符串都是,但各种字符串不是端对端放置)。因此释放字符串会产生问题,因为空间无法有效地重用。所以你随着你创造的每个tmp而成长。而且你永远不会重复使用任何东西。
使用基于数组的内爆,只创建1个输出 - 正好是您需要的长度。仅执行1次额外分配。因此它的内存效率更高,并且不会受到串联碎片的影响。 python也是如此。如果需要连接字符串,则多个连接应始终基于数组:
''.join(['str1','str2','str3'])
在python中
implode('', array('str1', 'str2', 'str3'))
PHP中的
sprintf等价物也没问题。
memory_get_peak_usage报告的内存基本上总是它必须使用的虚拟地图中的“最后”内存位。因此,由于它一直在增长,它报告了快速增长。因为每个分配都落在当前使用的内存块的“末尾”。
答案 1 :(得分:19)
在PHP> = 5.3.0中,您可以调用gc_collect_cycles()
强制GC通过。
注意:您需要在启用zend.enable_gc
时启用php.ini
,或致电gc_enable()
以激活循环参考收集器。
答案 2 :(得分:12)
找到解决方案:这是一个字符串连接。我通过连接一些变量逐行生成输入(输出是CSV文件)。但是,PHP似乎没有释放用于字符串的旧副本的内存,因此有效地破坏了RAM与未使用的数据。切换到基于数组的方法(并在将其输出到outfile之前用逗号进行插入)可以避免这种行为。
出于某种原因 - 对我来说不明显 - PHP报告了json_decode调用期间内存使用量的增加,这误导了我认为json_decode函数是问题的假设。
答案 3 :(得分:9)
我发现PHP的内部内存管理器最有可能在完成一个函数时被调用。知道了,我已经在循环中重构了代码:
while (condition) {
// do
// cool
// stuff
}
到
while (condition) {
do_cool_stuff();
}
function do_cool_stuff() {
// do
// cool
// stuff
}
修改强>
我运行了这个快速基准测试,没有看到内存使用量的增加。这让我相信泄漏不在json_decode()
for($x=0;$x<10000000;$x++)
{
do_something_cool();
}
function do_something_cool() {
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
$result = json_decode($json);
echo memory_get_peak_usage() . PHP_EOL;
}
答案 4 :(得分:6)
在每个语句后调用memory_get_peak_usage()
,并确保unset()
尽一切可能。如果您使用foreach()
进行迭代,请使用引用的变量来避免复制原始文件(foreach())。
foreach( $x as &$y)
如果PHP实际上泄漏了内存,强制垃圾收集将没有任何区别。
在IBM
上有一篇关于PHP内存泄漏及其检测的文章答案 5 :(得分:6)
我遇到了同样的问题并找到了可能的解决方法。
情况:我是从db查询写入csv文件的。我总是分配一个$行,然后在下一步重新分配它。取消$ row没有帮助;首先将5MB字符串放入$ row(以避免碎片)没有帮助;创建一个$ row-s数组(在其中加载许多行+在每5000步中取消整个行程)并没有帮助;真的尝试了几件事。
BUT。
当我创建一个打开文件的单独函数时,传输100.000行(刚好不要占用整个内存)并关闭文件,然后我对此函数进行了后续调用(附加到现有文件),I发现对于每个函数出口,PHP都删除了垃圾。这是一个局部可变空间的东西。
<强>结论:强> 每当函数退出时,它都会释放所有局部变量。
据我所知,这是规则。然而,只有一方需注意:当我试图让我的“do_only_a_smaller_subset()”函数通过引用获取一些变量(即查询对象和文件指针)时,垃圾收集没有发生。现在也许我误解了一些东西,也许查询对象(mysqli)漏了,好吧,我不知道。但是,由于它是由ref传递的,显然它无法清理,因为它存在于小函数的出口点之外。
所以,值得一试! 它节省了我的一天来找到它。
答案 6 :(得分:4)
我打算说我不一定会期望gc_collect_cycles()来解决问题 - 因为可能文件不再映射到zvars。但是你在加载任何文件之前是否检查过gc_enable被调用了?
我注意到PHP在执行包含时似乎吞噬了内存 - 远远超过源和标记化文件所需的内容 - 这可能是一个类似的问题。我不是说这是一个错误。
我相信一个解决方法是不使用file_get_contents而是使用fopen().... fgets()... fclose()而不是一次性将整个文件映射到内存中。但你需要尝试确认。
HTH
下进行。
答案 7 :(得分:3)
最近有similar issue System_Daemon。今天我将问题分离到了file_get_contents
。
您可以尝试使用fread
吗?我认为这可以解决你的问题。
如果确实如此,那么可能是时候在PHP上进行错误报告了。