在PHP中强制释放内存

在一个PHP程序中,我依次读取一堆文件(使用file_get_contents ), gzdecode它们进行json_decode对结果进行json_decodeparsing,分析内容,把大部分内容扔掉,并在数组中存储大约1%。 不幸的是,每次迭代(我遍历包含文件名的数组)时,似乎有一些内存丢失(根据memory_get_peak_usage ,每次大约2-10 MB)。 我有双重和三重检查我的代码,我没有存储循环中的无用数据(和所需的数据几乎不超过10MB总体),但我经常重写(实际上,数组中的string)。 显然,PHP没有正确释放内存,因此使用越来越多的内存,直到达到极限。 有没有办法强制垃圾收集? 或者至less要找出内存在哪里?

在此先感谢,德米特里

它与内存碎片有关。

考虑两个string,连接到一个string。 每个原稿必须保留,直到创build输出。 输出比任何一个input都要长。
因此,必须进行新的分配来存储这种级联的结果。 原始string被释放,但它们是小块的内存。
'str1' . 'str2' . 'str3' . 'str4'的情况下'str1' . 'str2' . 'str3' . 'str4' 'str1' . 'str2' . 'str3' . 'str4' 'str1' . 'str2' . 'str3' . 'str4'你有几个临时创build在每个。 – 他们没有一个适合在被释放的空间里。 由于内存的其他用途,这些string可能不是连续内存(即每个string都是,但各种string并不是端到端)布局的。 所以释放string会产生问题,因为空间不能被有效地重用。 所以你随着你创build的每个tmp而增长。 而且你永远不会重复使用任何东西。

使用基于数组的implode,您只能创build1个输出 – 正是您需要的长度。 只执行1个额外的分配。 所以它的内存效率要高得多,而且不会受到串联碎片的影响。 python也是如此。 如果你需要连接string,多于1个连接应该总是基于数组:

 ''.join(['str1','str2','str3']) 

在python

 implode('', array('str1', 'str2', 'str3')) 

在PHP中

sprintf等价物也很好。

memory_get_peak_usage报告的内存基本上总是它必须使用的虚拟映射中的“最后”位内存。 所以它一直在增长,它报告快速增长。 由于每个分配落在当前使用的存储器块的“末尾”。

在PHP> = 5.3.0中,您可以调用gc_collect_cycles()来强制GC传递。

注意:您需要启用php.ini gc_enable() ,或者调用gc_enable()来激活循环引用收集器。

find解决scheme:这是一个string连接。 我通过连接一些variables逐行生成input(输出是一个CSV文件)。 然而,PHP似乎不释放用于旧的string副本的内存,从而有效地使用未使用的数据来破坏RAM。 切换到一个基于数组的方法(并且在将其输出到outfile之前用逗号进行内插)避免了这种行为。

出于某种原因 – 对我来说不是很明显 – PHP在json_decode调用期间报告了增加的内存使用情况,这使我误以为json_decode函数是个问题。

我发现PHP的内部内存pipe理器最有可能在完成一个函数时被调用。 知道这一点,我已经重构了循环中的代码,如下所示:

 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; } 

在每个语句后调用memory_get_peak_usage() ,并确保你unset()你可以做的任何事情。 如果使用foreach()进行迭代,请使用引用variables来避免创build原始文件( foreach() )的副本。

 foreach( $x as &$y) 

如果PHP实际上正在泄漏内存,强制垃圾收集将不会产生任何影响。

在IBM有一篇关于PHP内存泄漏及其检测的好文章

我想说,我不一定会期望gc_collect_cycles()来解决问题 – 因为大概文件不再映射到zvars。 但是,您是否在加载任何文件之前检查了gc_enable是否被调用?

我注意到,PHP似乎吞噬内存时做的包括 – 远多于源和标记文件所需 – 这可能是一个类似的问题。 我不是说这是一个错误。

我相信一个解决方法是不使用file_get_contents而是使用fopen()…. fgets()… fclose()而不是一次性将整个文件映射到内存中。 但是,你需要尝试确认。

HTH

C。

我只是有同样的问题,并find一个可能的解决方法。

情况:我从一个数据库查询写入CSV文件。 我总是分配一个$行,然后在下一步重新分配。 取消设置$行并没有帮助; 先把一个5MB的string放到$行中(为了避免碎片)没有帮助; 创build一个$ row-s的数组(加载多行到每第5000个步骤都没有设置)没有帮助; 真的尝试了几件事情。

但。

当我做了一个单独的函数打开文件,传输100.000行(只是不吃掉整个内存),并closures文件,然后我做了后续调用这个函数(追加到现有的文件),我发现每个函数都退出,PHP清除垃圾。 这是一个局部variables空间的东西。

结论:只要你的函数退出,它就释放所有的局部variables。

就我所知,这是规则。 只是一方面说明:当我试图让我的“do_only_a_smaller_subset()”函数通过引用(即查询对象和文件指针)获取一些variables时,垃圾回收没有发生。 现在,也许我误解了一些东西,也许查询对象(mysqli)漏了,我不知道。 然而,由于它是通过了裁判,显然它不能被清理,因为它存在于小函数的退出点。

所以,值得一试! 它拯救了我的一天,发现了这一点。

最近有一个与System_Daemon 类似的问题 。 今天我把我的问题隔离到file_get_contents

你可以尝试使用fread吗? 我认为这可能会解决你的问题。 如果是这样,那么可能是在PHP上执行bugreport的时候了。