首页 > 解决方案 > 为什么在匿名函数上使用 `unset` 时使用 `eval` 以后不会释放内存?

问题描述

下面非常简单的 PHP 代码可以完美运行,我的意思是,它什么也不做,同时它几乎不使用任何系统内存。

<?php

set_time_limit(0); 
ini_set('memory_limit','-1');

for ($i=0;$i<100000000;$i++) {
        
    $bbb = function() {
    
        return true;

    };

    unset($bbb);
    
}

?>

虽然下面的代码非常相似,但是如果你运行它,几分钟后你会崩溃你的系统,因为它会消耗你所有的系统 RAM。

<?php

set_time_limit(0); 
ini_set('memory_limit','-1');

for ($i=0;$i<100000000;$i++) {
    
    eval('
    
        $bbb = function() {
        
            return true;
    
        };
    
    ');

    unset($bbb);
    
}

?>

我的实际$bbb功能要复杂得多,这是我的 AI 输出的结果,所以我真的需要eval在这种情况下使用。我完全了解安全隐患。

我只是想知道如何解决这个问题。在第二种情况下,如何让 PHP/APACHE 释放内存(GC 可能是 curlprit)?

注意:我正在使用带有 PHP 7.4 的 Windows 10

编辑

根据@Nigel Ren 的建议(看起来非常好),我尝试了这个:

<?php

set_time_limit(0); 
ini_set('memory_limit','-1');

for ($i=0;$i<100000000;$i++) {
    
    require("test.php");

    unset($bbb);
    
}

?>

“test.php”文件有这个:

<?php

$bbb = function() {

    return true;

};

?>

但是这个建议仍然消耗大量内存,它没有被清除!即使我使用gc_mem_caches();GC 仍然没有清除内存。也许我从你的建议@@Nigel Ren 中理解了一些错误?

标签: phpapache

解决方案


请参阅 Chris对此错误报告的最后评论。

为了后代的缘故,我会在这里复制它:

我最近对 ​​eval() 行为感兴趣,我可以确认到执行时内存使用率会更高。然而,这并不是一个真正的错误,而是 eval() 的基本编写方式:

  • 它创建一个包含代码的临时文件(有时它存储在内存中)
  • 然后它使用普通的 include() 函数包含该临时文件

如您所见,您执行的 eval() 越多,它将触发的 include() 越多,从而导致无法释放的内存使用...

因此,使用时内存使用量增加的原因eval是它包含临时文件。有多种方法可以取消包含文件,因此您使用eval的越多,您最终将使用的内存就越多。

免责声明:以下纯属猜测,我尚未验证这是否有效

至于解决此问题的潜在解决方法,既然您说您需要使用eval,您可以考虑使用扩展pcntl分叉一个进程。

它可能会更慢,但是如果您可以eval将代码的 -ing 部分分叉到一个单独的进程,该进程可以在完成后优雅地终止,那么应该清除在该子进程中执行的“包含”。这可能是限制应用程序中内存使用的一种方法。


推荐阅读