为什么此循环使用的峰值内存如此之高?

时间:2017-07-13 16:53:18

标签: php arrays multidimensional-array memory-management

我有这个PHP代码(版本5.4)读取6MB文本文件并将其拆分为行,然后按标签拆分这些行:

echo "Initial memory used: ".memory_get_usage()." bytes.\n";
$data = file_get_contents(ENROLLMENTS_SRC_PATH);
echo "About to split into lines, current memory used: ".memory_get_usage()." bytes.\n";
$data = explode("\n", $data);
echo "About to split lines into columns, current memory used: ".memory_get_usage()." bytes.\n"; 
$line = explode("\t", $data[0]);
echo "Split one line, current memory used: ".memory_get_usage()." bytes.\n";
echo "Peak memory used so far: ".memory_get_peak_usage()." bytes.\n";
foreach($data as $key => $line) {
    $data[$key] = explode("\t", $line);
}
echo "Final memory used: ".memory_get_usage()." bytes.\n";
echo "Peak memory used: ".memory_get_peak_usage()." bytes.\n";

据我所知,PHP数组的开销非常高,但根据这些结果,我并不期望foreach循环中的峰值使用量比最终结果大约28MB:

Initial memory used: 226384 bytes. 
About to split into lines, current memory used: 6536952 bytes.
About to split lines into columns, current memory used: 18327712 bytes. 
Split one line, current memory used: 18328352 bytes. 
Peak memory used so far: 24639744 bytes.
Final memory used: 116898184 bytes.
Peak memory used: 135000584 bytes.

这是如此之大,以至于峰值内存高于循环前和循环组合$data使用的内存,即使单个分割线的大小似乎低于千字节。我试图减少我的脚本使用的内存,所以我试图了解PHP如何使用内存。

为什么foreach循环使用了这么多明显多余的内存,我能做些什么呢?

1 个答案:

答案 0 :(得分:0)

大多数超额记忆背后的解释都在this explanation of the workings of foreach中找到,这解释了foreach适用于"副本"数组。该副本实际上是一个写时复制,其中只有在/修改数组时才会发生重复。由于这个foreach循环确实修改了原始数组,因此它会创建一个循环的副本,这解释了大部分额外的28MB。

有几种方法可以防止这种情况,包括使用除foreach之外的任何其他循环,或使用如下引用:

foreach($data as &$line) {
    $line = explode("\t", $line);
}
unset($line)

当循环的数组或包含数组条目的变量是引用时,不会进行重复。但是,重要的是取消设置$line以防止在循环运行后意外修改数组的内容。