使用 unset() 来节省内存

Use unset() to save memory

背景:我需要逐行解析一些大的XML文件并将信息保存在关联数组中。我正在用 DOMDocument.

解析它

即使内存节省不是基本要求,我也在尝试使用 unset() 在我的脚本执行期间节省一些内存,以避免在不使用 ini_set 的情况下可能出现的此类错误方法和类似的事情。

请参阅下面的代码以了解我执行此操作的方法:

        //using DOMDocument to get the tag product
        $countriesCovered = array();
        $countriesCoveredTag = $productTag->getElementsByTagName('countriesCovered')->item(0);
        $countries = $countriesCoveredTag->getElementsByTagName('country');
        foreach ($countries as $countryTag) {
            $country = array();
            $country['name'] = $countryTag->getElementsByTagName('name')->item(0)->nodeValue;
            $country['code'] = $countryTag->getElementsByTagName('code')->item(0)->nodeValue;
            $countriesCovered[] = $country;
            unset ($country);
        }

对我来说,这样做节省内存是合乎逻辑的,因为我正在将变量 country 复制到数组 countriesCovered 并取消设置 country (我的意思是释放分配给 country 的内存,对吗?)。但是,我在 Documentation 中没有找到任何东西来确保这一点,所以我无法确保我真的在节省内存。

因此, 我这样做的方式正确吗? 即使 Garbage Collection 也需要它吗?我认为 unset 可能完全没有价值,但我不能保证这一点。

你可以在没有临时变量的情况下做到这一点

$countriesCovered[] = [
    'name' => $countryTag->getElementsByTagName('name')->item(0)->nodeValue,
    'code' => $countryTag->getElementsByTagName('code')->item(0)->nodeValue
]

这不完全正确。 PHP 没有任何内置内存管理机制。它完全依赖于垃圾收集,因此要真正释放任何内存,您需要调用垃圾收集过程。 Unset 实际上只是递减内部引用计数器而不对内存本身做任何事情。一旦引用计数器达到零,垃圾收集器将释放内存。

您可能想在 composer 的案例研究中查看垃圾收集器如何影响应用程序性能。查看 how one line change caused composer to run 70% faster

对于你的情况,取消设置实际上什么都不做。 PHP 内部不会在赋值后立即复制变量。它改为创建一个引用并增加内部引用计数器。变量只有在赋值后被修改时才真正被复制。 因此您的 $country 变量指向与 $countriesCovered 数组中的项目相同的内存地址

正如 Alexander Madyuskin 所说,您不需要在循环内声明变量;你可以直接将变量设置到你的主关联数组中。无论如何,您不会丢失太多内存,因为您实际上只是在创建几个引用(而不是多个值)。

即使您正在操纵值,这会导致创建 value 的多个副本(与多个引用相反),局部变量也会超出范围反正每次迭代结束;所以调用 unset 是不必要的。

一般垃圾收集

您所询问的关于垃圾收集的更广泛的观点是:

The garbage collector will free up memory on its own when variables fall out of scope

当没有可访问的引用时,变量被认为超出范围;因此在 foreach 循环或 if 条件中定义的变量有资格在迭代或条件结束时收集。

示例:

$externalArr = [];
while ($x=0; $x<50; $x++) {
  $internalArr = []
  $externalArr[] = $x;
  $internalArr[] = $x;

  // End of loop, $internalArr is now eligible for 
  // garbage collection regardless of whether it is unset
}

// The $externalArr is still in scope, so if we want it to be 
// collected, we have to manually unset it, or else it will exist 
// until the end of the script execution
unset($externalArr);