memory_get_peak_usage()与“实际使用情况”的含义是什么?

104
如果将real_usage参数设置为true,PHP文档说它将获取从系统分配的实际内存大小。如果设置为false,它将获取emalloc()报告的内存。

这两个选项中哪一个返回相对于php.ini中的内存限制值分配的最大内存?
我想知道脚本接近达到该限制的程度。

9
我想向您介绍Julien Pauli在2013年php uk大会上的演讲,视频链接为http://www.youtube.com/watch?v=sm1HUrnsxLI。他在演讲中讲解了PHP内存如何工作。 - mpratt
另请参见http://stackoverflow.com/a/7234026/632951。 - Pacerier
5个回答

156

好的,让我们使用一个简单的脚本进行测试:

ini_set('memory_limit', '1M');
$x = '';
while(true) {
  echo "not real: ".(memory_get_peak_usage(false)/1024/1024)." MiB\n";
  echo "real: ".(memory_get_peak_usage(true)/1024/1024)." MiB\n\n";
  $x .= str_repeat(' ', 1024*25); //store 25kb more to string
}

输出:

not real: 0.73469543457031 MiB
real: 0.75 MiB

not real: 0.75910949707031 MiB
real: 1 MiB

...

not real: 0.95442199707031 MiB
real: 1 MiB

not real: 0.97883605957031 MiB
real: 1 MiB

PHP Fatal error:  Allowed memory size of 1048576 bytes exhausted (tried to allocate 793601 bytes) in /home/niko/test.php on line 7

似乎真正的使用是从系统分配的内存,这些内存以比脚本当前需要的更大的块分配(我想是出于性能原因)。这也是php进程使用的内存。

$real_usage = false 是你脚本实际使用的内存,而不是Zend内存管理器分配的实际内存量。

阅读此问题获取更多信息。

简而言之:要了解你与内存限制的接近程度,请使用$real_usage = true


8
Zend引擎以256K块分配内存,"真实使用"的值是所有这些块的总和。这实际上是用来触发内存耗尽错误的值: if (segment_size < true_size || heap->real_size + segment_size > heap->limit) { /* Memory limit overflow */. - cleong
2
“非真实” 值是由对 emalloc 的调用请求的字节数之和(加上头部和内存对齐所需的字节数)组成。它不反映由于块不适合已分配段中剩余空间而浪费的内存。如果将示例更改为分配(1024 * 256)字节和2M限制,则两者之间的差异将变得更加明显。 - cleong
@Niko,你为什么使用memory_get_peak_usage而不是memory_get_usage?我们不应该gc_disable()并使用memory_get_usage来获得更准确的结果吗? - Pacerier
@Pacerier 这个问题是要了解脚本接近极限的程度 - 对于这一点,峰值是有意义的。 - Niko Sams
我认为最好写成if(ini_set('memory_limit', '1M')),这样那些喜欢复制粘贴并已将memory_limit设置为-1且ini_set不起作用的人也可以使用它 :) - h0mayun
6
如@cleong所解释的那样,尽管得到了许多赞,但这个答案实际上是错误的。memory_get_usage(true)返回的值应该和memory_limit进行比较。答案中给出的例子太简单了,因为没有“浪费”的内存。发生的情况是,“真正”分配的内存需要从“1 MiB”增加到“1.25 MiB”,这就触发了致命错误。我有一个复杂的批处理脚本,内存限制为120 MiB,但在被中止时,“未使用的”已分配内存只有“80 MiB”,因为“真正”的已分配内存达到了极限。 - Martin Prikryl

43

介绍

你应该使用 memory_get_usage(false),因为你想要的是已使用的内存而不是已分配的内存。

有什么区别

你的 Google Mail 可能为你分配了 25MB 的存储空间,但这并不意味着您当前已经使用了那么多。

这正是 PHP 文档所说的。

将此设置为 TRUE 以获取从系统分配的内存的实际大小。如果未设置或设置为 FALSE,则仅报告 emalloc() 使用的内存。

两个参数都会返回相对于内存限制的已分配内存,但主要区别是:

memory_get_usage(false) 给出由 emalloc() 使用的内存,而 memory_get_usage(true) 返回里程碑,可以在此处演示 Memory Mile Store

我想知道脚本接近达到限制的程度有多近。

这需要进行一些数学计算,可能只适用于循环或特定用例。我为什么这么说?

想象一下

ini_set('memory_limit', '1M');
$data = str_repeat(' ', 1024 * 1024);

即使在检查内存之前,上述脚本就会失败。

据我所知,我可以检查PHP变量或特定部分使用的内存的唯一方法是:

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

查看解释,但如果您处于循环或递归函数中,您可以使用最大内存使用量来安全地估计何时达到内存峰值。

示例

ini_set('memory_limit', '1M');

$memoryAvailable = filter_var(ini_get("memory_limit"), FILTER_SANITIZE_NUMBER_INT);
$memoryAvailable = $memoryAvailable * 1024 * 1024;

$peakPoint = 90; // 90%

$memoryStart = memory_get_peak_usage(false);
$memoryDiff = 0;

// Some stats
$stat = array(
        "HIGHEST_MEMORY" => 0,
        "HIGHEST_DIFF" => 0,
        "PERCENTAGE_BREAK" => 0,
        "AVERAGE" => array(),
        "LOOPS" => 0
);

$data = "";
$i = 0;
while ( true ) {
    $i ++;
    
    // Get used memory
    $memoryUsed = memory_get_peak_usage(false);
    
    // Get Difference
    $memoryDiff = $memoryUsed - $memoryStart;
    
    // Start memory Usage again
    $memoryStart = memory_get_peak_usage(false);
    
    // Gather some stats
    $stat['HIGHEST_MEMORY'] = $memoryUsed > $stat['HIGHEST_MEMORY'] ? $memoryUsed : $stat['HIGHEST_MEMORY'];
    $stat['HIGHEST_DIFF'] = $memoryDiff > $stat['HIGHEST_DIFF'] ? $memoryDiff : $stat['HIGHEST_DIFF'];
    $stat['AVERAGE'][] = $memoryDiff;
    $stat['LOOPS'] ++;
    $percentage = (($memoryUsed + $stat['HIGHEST_DIFF']) / $memoryAvailable) * 100;
    
    // var_dump($percentage, $memoryDiff);
    
    // Stop your script
    if ($percentage > $peakPoint) {
        
        print(sprintf("Stoped at: %0.2f", $percentage) . "%\n");
        $stat['AVERAGE'] = array_sum($stat['AVERAGE']) / count($stat['AVERAGE']);
        $stat = array_map(function ($v) {
            return sprintf("%0.2f", $v / (1024 * 1024));
        }, $stat);
        $stat['LOOPS'] = $i;
        $stat['PERCENTAGE_BREAK'] = sprintf("%0.2f", $percentage) . "%";
        echo json_encode($stat, 128);
        break;
    }
    
    $data .= str_repeat(' ', 1024 * 25); // 1kb every time
}

输出

Stoped at: 95.86%
{
    "HIGHEST_MEMORY": "0.71",
    "HIGHEST_DIFF": "0.24",
    "PERCENTAGE_BREAK": "95.86%",
    "AVERAGE": "0.04",
    "LOOPS": 11
}

演示

这可能仍然失败

它可能会失败,因为在 if ($percentage > $peakPoint) { 之后还需要执行其他任务,这也会消耗内存。

        print(sprintf("Stoped at: %0.2f", $percentage) . "%\n");
        $stat['AVERAGE'] = array_sum($stat['AVERAGE']) / count($stat['AVERAGE']);
        $stat = array_map(function ($v) {
            return sprintf("%0.2f", $v / (1024 * 1024));
        }, $stat);
        $stat['LOOPS'] = $i;
        $stat['PERCENTAGE_BREAK'] = sprintf("%0.2f", $percentage) . "%";
        echo json_encode($stat, 128);
        break;

如果处理此请求所需的内存大于可用内存,则脚本将失败。

结论

这不是一个完美的解决方案,但可以在间隔期间检查内存使用情况,如果超过峰值(例如90%),立即退出并放弃一些复杂功能。


1
memory_limit选项是关于堆还是栈的? - Yousha Aleayoub
如果我有两个脚本并行运行或者有多个请求,那么memory_get_usage()函数返回的是所有同时执行的脚本所使用的内存还是仅仅当前脚本所使用的内存? - Yassine CHABLI

7

real_usage false会报告脚本所使用的内存。这是两者中更准确的一个。

real_usage true会报告分配给您的脚本的内存。这将是两者中更高的。

如果要进行比较,我可能会使用true,因为您的脚本永远不会被分配超过内存限制,并且只要它(加上所有其他脚本)没有超过该使用量,它就会继续运行。


1
这恰恰相反:false 是脚本使用的内存,而 true 是分配的内存。 - BenMorel
1
@Benjamin 是啊,不太确定为什么我犯了这么个低级错误。嗯,已经修正了。 - Glitch Desire

2
根据 PHP memory_get_usage,若设置 real_usage 为 TRUE,则可以获取系统分配的总内存,包括未使用的页面。若未设置或设置为 FALSE,则只报告已使用的内存。
因此,要获取脚本使用的内存,应默认使用 memory_get_usage(),real_usage 为 false。
如果想获取系统分配的内存,但不关心实际使用了多少,可以使用 memory_get_usage(true)。

-1
<!-- Print CPU memory and load -->
<?php
$output = shell_exec('free');
$data = substr($output,111,19);
echo $data;
echo file_get_contents('/proc/loadavg');
$load = sys_getloadavg();
$res = implode("",$load);
echo $res;
?>

2
欢迎来到Stackoverflow!您能告诉我们答案是什么吗?不仅仅是代码,还有您是如何找出问题并解决它的过程。谢谢! - Gilles Heinesch
虽然您的回答可能提供了一些潜在有用的信息,但并不与所提出的问题相关。您或许需要解释一下您的回答如何与所提出的问题相关联。 - Moritur

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