PHP 内存取消设置带有对象的数组


PHP memory unsetting array with objects

测试 1:

class Entity { }
$entities = array();
echo "START: ". number_format(memory_get_usage()) . "'n";
for($i = 0; $i < 100000; ++$i)
{
    $entities[] = new Entity();
}
echo "BEFORE UNSET: ". number_format(memory_get_usage()) . "'n";
unset($entities);
echo "AFTER UNSET: ". number_format(memory_get_usage()) . "'n";

输出:

START: 631,664
BEFORE UNSET:44,404,904
AFTER UNSET: 8,954,568

测试二:

class Entity { }
$entities = array();
echo "START: ". number_format(memory_get_usage()) . "'n";
for($i = 0; $i < 100000; ++$i)
{
    $entity = new Entity();
    $entities[] = &$entity;
}
echo "BEFORE UNSET: ". number_format(memory_get_usage()) . "'n";
unset($entities);
echo "AFTER UNSET: ". number_format(memory_get_usage()) . "'n";

输出:

START: 631,664
BEFORE UNSET: 10,480,480
AFTER UNSET: 631,752

在我遇到内存限制问题后,我玩了一下......

所以在那之后我想知道垃圾收集是如何工作的:

  1. 为什么测试比测试需要更多的内存?
  2. 为什么 php 在测试中取消设置数组后会保留内存?

谢谢戴夫

  1. 第一个您将实际对象保存到数组中的每个值,因此当您取消设置数组时,这些对象仍然存在。
    • 这就是为什么这个数组占用更多的内存——它存储实际的对象
  2. 第二个你只是保存对对象的引用(仅在for循环的范围内),一旦取消设置,对象就消失了。
    • 此数组占用较少内存的原因是它只存储对对象的引用

因此,如果您执行此处的操作:

$entities = array();
echo "START: ". number_format(memory_get_usage()) . "'n";
for($i = 0; $i < 100000; ++$i)
{
    $entities[] = new Entity;
}
echo "BEFORE UNSET: ". number_format(memory_get_usage()) . "'n";
foreach($entities as &$entity) {
    unset($entity);
}
unset($entities);
echo "AFTER UNSET: ". number_format(memory_get_usage()) . "'n";

然后取消设置数组中的每个对象。您得到的结果与第二次尝试相同:

例:

起价: 3,237,720
未设置前:21,297,640
未设置后:3,237,488

演示:http://codepad.org/oUjzA46D

为什么测试一比测试二需要更多的内存?

PHP 分配的

内存来存储标识符比为引用分配的内存多(您的第一个测试按标识符分配)。

从手册:

PHP 引用是一个别名,它允许两个不同的变量 写入相同的值。从 PHP 5 开始,对象变量不会 将对象本身作为值再包含。它只包含一个对象 标识符,允许对象访问器查找实际对象。 当一个对象通过参数发送、返回或分配给另一个对象时 变量,不同的变量不是别名:它们保存一个副本 指向同一对象的标识符。

这使得标识符有些神奇,并且它们是专门处理的。

为什么 php 在测试一中取消设置数组后会保留内存?

unset只是标记变量的内存以通过GC的算法释放;它不会立即释放内存。当我运行您的第一个测试时,释放了大量内存(尽管没有第二次测试那么多,可能是由于分配了更大的大小)。