【问题标题】:Out of memory although memory_limit is large enough尽管 memory_limit 足够大,但内存不足
【发布时间】:2015-02-12 23:50:16
【问题描述】:

考虑以下简单的 PHP 脚本:

<?php
print memory_get_usage()."<br />";
print ini_get("memory_limit")."<br />";
$file = imagecreatefromjpeg("image.jpg");
?>

输出如下:

109848
120M

Fatal error: Out of memory (allocated 63700992) (tried to allocate 23040 bytes) in /homepages/13/d444038670/htdocs/bilderarchiv/test.php on line 4

我尝试阅读的图片是一张大图。但是,第二行声明 memory_limit 为 120 MB,但脚本在分配大约 64 MB 时终止。怎么会这样?该脚本在另一个提供程序上完美运行,尽管 memory_limit 也是 120M。

【问题讨论】:

  • 图片的尺寸和颜色模式是多少? JPEG 图片在加载时会“解压缩”。而且它们在内存中需要的字节数比在硬盘上的多。
  • 尺寸为 5760 x 3240,24 位 sRGB。
  • 跟踪真实内存使用情况:memory_get_usage(true) — 参见docsthis question
  • 实际内存使用量为 262144。只有几千字节,这并不奇怪,因为它是文件中的第一条语句。然而,特殊之处在于 PHP 在大约 64MB 时死掉了,尽管内存限制为 120MB。就是这个问题。

标签: php out-of-memory gd


【解决方案1】:

您的内存不足,但有一个解决方案。

尝试在您的两台服务器上运行此程序,以确定每个图像像素需要多少内存:

    <?php
    $mem0 = memory_get_usage(true);
    $megapix = imagecreatetruecolor(1000,1000);
    $mem1 = memory_get_usage(true);
    $memdif = $mem1 - $mem0;
    $perpixel = $memdif / (1000 * 1000);
    echo "<pre>1 million pixel memory test
    before imagecreate: $mem0
     after imagecreate: $mem1
     image memory used: $memdif
       bytes per pixel: $perpixel";
    ?>

我得到了这些结果:

    1 million pixel memory test  (Linux)
    before imagecreate: 524288
     after imagecreate: 5505024
     image memory used: 4980736
       bytes per pixel: 4.980736

    1 million pixel memory test (Windows)
    before imagecreate: 262144
     after imagecreate: 5505024
     image memory used: 5242880
       bytes per pixel: 5.24288

每像素 5 个字节,您的 5760x3240 图像将需要 89MB。

要查看 imagecreatefromjpeg 是否需要超出图像所需的 89MB 的大量临时内存,请在加载图像后将memory_get_usagememory_get_peak_usage 进行比较。在我的测试中,没有显着性。

(我认为旧版本的 GD 使用了 4 个字节。)

接下来,看看你到底有多少内存:

    <?php
    echo 'memory_limit ??? ', ini_get('memory_limit'), '<br>';
    $megs = Array();
    for ( $i=0; $i < 1000; ++$i ) {     // try up to 1000MB
        $megs[] = str_repeat('*', 1048000);
        $mb= round(memory_get_usage(true) / 1024/1024);
        echo "$mb "; 
        flush();
        ob_flush();
    }
    ?>

如果您根本没有得到任何输出,则可能存在无法禁用的缓冲,因此只需在 for 循环中尝试不同的上限,直到找到仍然有效的最大上限。

一个解决方案

我很久以前就放弃了 GD 来缩小大型 jpeg 图像,并改用 ImageMagick。如果您从 PHP 中执行它,它是一个单独的进程,因此 PHP 内存限制不再适用。当然最终会有内存限制,但你可以告诉 ImageMagick 使用的最大内存。

(我的图像质量也比 GD 好。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2013-11-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 2022-11-11
    • 1970-01-01
    相关资源
    最近更新 更多