我有一个16 MB大小的CSV文件,并尝试解析它,并做一些事情,但脚本在一段时间后内存不足。我意识到这段代码会产生大约200 MB的已用空间,并且unset不起作用。
$countRows = 1;
var_dump("3. ".memory_get_usage()." beginDiff: ".(memory_get_usage() - $this->startingMemory));
while(($row = fgetcsv($fp, 300000, ';', '"')) !== FALSE)
{
if ($row == '')
continue;
if($firstRow == true)
{
foreach($row as $k => $v)
{
$this->columnMapping[$k] = trim(mb_strtolower($v));
}
$firstRow = false;
continue;
}else
{
foreach($row as $k => $v)
{
$row[$this->columnMapping[$k]] = $v;
unset($row[$k]);
}
}
...
//$this->theirCategoriesToProducts[$row['kategorie']][]['kodproduktu'] = $row['kodproduktu'];
$this->theirCategoriesToProducts[$row['kategorie']][] = $row;
}
var_dump("3,5. ".memory_get_usage()." beginDiff: ".(memory_get_usage() - $this->startingMemory));
...
var_dump("7. - before unset total: ".memory_get_usage()." beginDiff: ".(memory_get_usage() - $this->startingMemory));
unset($this->theirCategoriesToProducts);
var_dump("8. - after unset total: ".memory_get_usage()." beginDiff: ".(memory_get_usage() - $this->startingMemory));die;
生成此输出:
string '3. 72417440 beginDiff: 34730040' (length=31)
string '3,5. 292748528 beginDiff: 255061136' (length=36)
string '7. - before unset total: 299039360 beginDiff: 261351984' (length=55)
string '8. - after unset total: 297364432 beginDiff: 259677056' (length=54)
设置变量等于null是输出非常相似。但是在这两行之间切换评论
$this->theirCategoriesToProducts[$row['kategorie']][]['kodproduktu'] = $row['kodproduktu'];
//$this->theirCategoriesToProducts[$row['kategorie']][] = $row;
将输出:
string '3. 72417784 beginDiff: 34730040' (length=31)
string '3,5. 81081984 beginDiff: 43394248' (length=34)
string '7. - before unset total: 87256544 beginDiff: 49568824' (length=53)
string '8. - after unset total: 85581520 beginDiff: 47893800' (length=52)
所以它的大约200 MB的“丢失”内存(几乎是专用的一半)。
递归函数unseting数组的所有部分都会占用更多的内存,而且还可以免费崩溃。
在脚本中永远不会使用带有&的数组所以不应该引用其他变量。
文件在3.5转储后立即关闭。
任何其他想法,如何取消设置该阵列?
答案 0 :(得分:5)
从PHP开始> 5.3有一些Garbage Collection机制可用,所以理论上你可以想到类似于文档中的例子
//Memory cleanup for long-running scripts.
gc_enable(); // Enable Garbage Collector
var_dump(gc_enabled()); // true
var_dump(gc_collect_cycles()); // # of elements cleaned up
gc_disable(); // Disable Garbage Collector
但不幸的是,在你的情况下你必须记住(根据Can I trigger PHP garbage collection to happen automatically if I have circular references?)垃圾收集器“不会运行,例如,当内存限制即将达到时。因此,你的在达到内存限制时,脚本仍然可以中止,因为在这种情况下,PHP太笨了而无法收集周期!“
最后,您可以尝试使用GC,但它可能无法解决您的问题。
那么,还有什么可以尝试的?尝试将导入的主数据阵列拆分为较小的块,然后依次逐个导入它们。将循环中的块始终提取到同一个变量中,然后遍历它以处理记录。
答案 1 :(得分:2)
PHP函数fgetcsv
是坏的,因为服务器需要将完整的文件存储在内存中更好地读取一行存储它
php数组使用了很多内存,因为php数组中的数组实现为“hashmaps”或“hashtable”,你可以使用splFixedArray
(真正的C或C ++数组),如果你不需要字符串作为键
splFixedArray
(你需要至少使用php 5.3才能使用它)已知使用php数组所需的总价格的40%。
答案 2 :(得分:1)
您可以使用unset
删除变量,以便对其进行垃圾回收。
$foo = "bar";
unset($foo);
var_dump($foo); // null
总体而言,只需跟踪您所引用的内容。也许你不需要跟踪所有事情。 while
- 循环允许您保持内存效率,只是为每条线路提供所需的内容。
有些脚本实际上只需要大量的内存来运行,增加内存限制并不是太疯狂,但只有在实际需要时才这样做。
答案 3 :(得分:0)
在这种情况下,在跳过的行中发现了问题。其中一个使用函数是隐藏函数,将数组的每个部分分配给缓存全局变量。删除此缓存变量解决了这个问题。