为什么debug_backtrace()会占用如此多的内存?

5
在尝试跟踪PHP中的一些内存问题时,我注意到我的日志代码中调用的debug_backtrace()似乎占用了大量内存。在大多数情况下,以下代码将打印类似于0.02 MB的内容。但在某些情况下,它会打印171.85 MB的内容!
$before = memory_get_usage();
$backtrace = debug_backtrace(false);
$after = memory_get_usage();
echo round(($after - $before)/1024/1024, 2)." MB";

我的问题是,这是否意味着debug_backtrace实际上使用了那么多内存?或者可能发生了其他事情,比如垃圾回收,从而混淆了memory_get_usage的返回值?

3个回答

7
很可能是对象导致了膨胀。尝试将 false 传递给函数,这样就不会拉取对象,您的跟踪信息将更小。
编辑:如果传递 false 不起作用,那么如果您正在运行 PHP 5.3.6+,则可以使用位掩码来限制函数返回的内容。听起来你传递的参数是巨大的对象。 http://php.net/manual/en/function.debug-backtrace.php 参考 此外,如果您使用的是 PHP 5.4.0+,它们添加了第二个参数,允许您限制堆栈帧数。
编辑2:这里是一个总体上的 <>,但是它可以工作... 添加 try/catch,抛出异常并捕获它,然后转换为字符串或调用 exception getTraceAsString() 来获取完整的堆栈信息。例如:
try {
    throw new Exception('ignore this string');
} catch(Exception $e) {
    /* @var $trace array */
    $trace = $e->getTrace();

    // OR

    /* @var $str string */
    $str = $e->getTraceAsString();
    $e = null;
}

在上面的片段中,您可以使用$trace构建自己的输出,或者只使用标准异常作为字符串$str。更容易获取堆栈帧输出。

这很有道理,但我刚试了一下,仍然出现了巨大的内存增加。 - JW.
@JW 在代码中添加了一个<<<***HACK***>>>,使用了 Exception - Yzmir Ramirez
这可以不使用try catch来完成,像这样: $e = new Exception(); $value = $e->getTraceAsString(); - dowi

4

好的,我想我已经弄清楚了。我打印出了回溯信息,发现"args"数组很大。这是因为我正在传递一些巨大的字符串。当返回结果时,它似乎在复制它们(而不是引用)。

例如:

function test($str) {
    test2($str);
}
function test2($str) {
    test3($str);
}
function test3($str) {
    echo "before: ".round(memory_get_usage()/1024/1024, 2)." MB\n";
    debug_backtrace(false);
    echo "after: ".round(memory_get_usage()/1024/1024, 2)." MB\n";
}
test(str_repeat('a', 10000000));

不加debug_backtrace()调用,试试看。此时,内存使用量会减少约28 MB。只有test3()返回时才会被清除。

1
我认为你在考虑按引用传递和按值传递。debug_backtrace()的输出是一个值数组。如果你使用字符串"Hello World!!!"递归调用一个函数100次,那么这个字符串的副本将会出现在数组中100次。但在你的应用程序中,它只有一个实例,并且有100个对它的引用,直到被修改。 - Yzmir Ramirez
很高兴你找到了解决方案。 - Yzmir Ramirez
2
不确定为什么会被踩...这明显是导致问题的原因。 - JW.
3
从 PHP 5.3.6 开始,你可以通过将 DEBUG_BACKTRACE_IGNORE_ARGS 传递给 debug_backtrace 来避免发生这种情况! :D 之前: 9.84 MB 之后: 9.84 MB - David Winiecki

2
如果你的代码是递归的,而且你已经深入到递归中,回溯将必须为每个递归存储数据...

我尝试在大内存增加后打印出$backtrace的计数,这应该表示函数调用的层数,但只有8个。 - JW.
@JW:嗯,抱歉。在这种情况下,我对此毫无头绪。 - 0xC0000022L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接