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