【发布时间】:2015-01-11 18:00:32
【问题描述】:
我试图弄清楚 PHP 是如何将数组加载到内存中的,以及传递数组何时会消耗内存。
所以我运行了这段代码:注意输入数组在这个例子中不太重要:
<?php
echo $this->getMemoryUsage();
$arr = $query->result_array(); // array of arrays from codeigniter
echo $this->getMemoryUsage();
这正好消耗 250 kB 的内存,这意味着数组的大小大约为 250 kB,大约。
所以我运行了以下代码:
<?php
echo $this->getMemoryUsage();
$arr = $query->result_array(); // array of arrays from codeigniter
$arr[0]['id'] = 'changing this value';
$foo = $arr;
$foo[2]['id'] = 'changing this value again';
$bar = $foo;
$bar[4]['id'] = 'changing this value again and again';
$far = $bar;
$far[5]['id'] = 'changing this value again and again and again';
echo $this->getMemoryUsage();
根据我阅读和被告知的内容,PHP 实际上并没有复制数组,它只引用原始数组,但是一旦进行了更改,PHP 就必须复制整个数组。
想象一下,当上面的代码恰好消耗 500 kB 的 RAM 时,我会感到惊讶。
谁能解释这里发生了什么?
为了清楚起见,所有这些索引(0-5 和id)已经存在于原始数组中,我只是在修改值。原始值是某个整数。
编辑
只是为了清除 $this->result() 的参与;这是我进行的另一项测试:
echo $this->getMemoryUsage();
$arr = $query->result_array(); // array of arrays from codeigniter
//$arr[0]['id'] = 'changing this value';
$foo = $arr;
$foo[2]['id'] = 'changing this value again';
//$bar = $foo;
//$bar[4]['id'] = 'changing this value again and again';
//
//$far = $bar;
//$far[4]['id'] = 'changing this value again and again and again';
echo $this->getMemoryUsage();
这次的输出正好是 250 kB - 就像原来的试用版一样,没有任何变化
编辑#2
根据要求,我在设置中运行了此处的代码,以确保结果一致: http://pastebin.com/cYNg4cg7
这些是结果:
声明:4608 kB
最终:8904 KB
与声明的差异:4296 kB
因此,即使声明为 4608 并且数组被传递和更改了 4 次,它仍然只增加了不到一倍的内存占用。
编辑#3
我已经在每次分配后运行了内存更改:
声明:5144 kB
分配 A0 添加:144 kB
分配 A1 添加:1768 kB
分配 A2 添加:1768 kB
分配 A3 添加:1768 kB
最终:10744 KB
声明的差异:5600 kB
第一次之后的每个后续操作的成本完全相同,这似乎表明正在复制完全相同的大小。这似乎支持奥斯汀的回答,现在唯一没有加起来的是分配的大小,但这是一个不同的问题。
看来奥斯汀很喜欢,如果没有其他答案,我会接受。
【问题讨论】:
-
非常棘手的问题,你可能对我前几天读到的以下文章感兴趣:nikic.github.io/2011/12/12/…
-
我几周前读过那篇文章,说实话很吸引人,但没有解释复制的具体工作原理。
-
我知道,只是觉得您可能会喜欢它。我不能回答你的问题,也不能给你一个可以回答你问题的链接。相反,我为你的问题加了星标,这样我就可以关注它,如果没有发布答案,我会给它一个赏金,因为我也很想知道这一点。 :)
-
我很想知道更多关于幕后发生的事情。在相关说明中,如果您担心“数组”(真正的哈希表)的内存使用情况,您可以使用 SplFixedArray 来获取“真实”数组:php.net/manual/en/class.splfixedarray.php
-
数组是否真的消耗了 250Kb,或者这也可能是调用的开销(由
result_array()方法分配的东西,并且(尚未)清理)。可以肯定的是,我会制作一个数组的新副本并测量该副本之前和之后的差异,尽管这也不是完全防水的。