Gd-png无法分配图像数据


gd-png cannot allocate image data

我有一个广泛使用GD的旧代码库(不,切换到imagemagick是不可能的)。它已经运行了好几年,经历了几个版本。但是,当我在当前的开发环境中运行它时,我遇到了一个神秘的gd-png错误:在调用imagecreatefrompng()时无法分配图像数据错误。我使用的PNG文件和我一直使用的是一样的,所以我知道它是有效的。

当前设置:

Ansible-provisioned vagrant box  
Ubuntu 14.04  
PHP 5.5.9  
GD 2.1.1  
libPNG 1.2.50

运行脚本时PHP的内存限制是650M,尽管最终是内核本身终止了脚本,并且更改PHP的内存限制似乎没有影响。

映像尺寸为7200x6600,磁盘上大约有500KiB。这在我的其他环境中不是问题,只是在我的开发环境中刚刚出现。不幸的是,我不能再访问其他环境来做比较,尽管设置与上一个工作环境相似——Ubuntu 14.04, PHP 5.5,足够的内存分配。

在这个设置中可能发生了什么在我以前的设置中没有发生的事情?我该如何解决这个问题?

我通过PHP 5.5.9源代码浏览了一下,试图找到您的特定错误字符串"无法分配图像数据",但我能找到的最接近的是"gd-png错误:无法分配gdImage结构"。PHP 5.5.9(一直到7.0.0)的GD捆绑版本是2.0.35,所以看起来您正在查看自定义构建(?)。麻烦PHP的家伙在这里可能没有帮助。

查看GD源(2.1.2,似乎找不到2.1.1),发生此错误的地方是:https://github.com/libgd/libgd/blob/master/src/gd_png.c L435

image_data = (png_bytep) gdMalloc (rowbytes * height);
if (!image_data) {
    gd_error("gd-png error: cannot allocate image data'n");
    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
    if (im) {
        gdImageDestroy(im);
    }
    if (palette_allocated) {
        gdFree (palette);
    }
    return NULL;
}

其中gdMalloc为:

https://github.com/libgd/libgd/blob/gd - 2.1 -/- src/gdhelpers.c # L73

 void *
 gdMalloc (size_t size)
 {
      return malloc (size);
 }
恐怕我的侦探工作就到此为止了。据我所知,PHP中捆绑的2.0.35 GD版本也只是使用malloc,所以乍一看并没有什么真正的区别。我还试图在捆绑版本中找到一些等效的代码,,但到目前为止我还没有找到它,它似乎在这里:

https://github.com/php/php-src/blob/PHP-5.5.9/ext/gd/libgd/gd_png.c L323

 image_data = (png_bytep) safe_emalloc(rowbytes, height, 0);

我似乎找不到safe_emalloc,但似乎旧的PHP捆绑版本确实使用了不同的内存分配,而不是您的环境使用的版本。也许你最好的办法是和GD的开发人员核对一下。为了避免白费力气,我认为在确认他们确实在使用非标准的GD版本之后,尝试另一个环境是你最好的选择。

在一些PHP源代码safari之后,似乎safe_emalloc在所有扩展中都使用,所以我猜(猜测请注意)这是为PHP扩展分配内存的首选方式(看起来像)。如果您的环境实际上使用的是未经修改的GD 2.1.1,那么它很可能会忽略任何PHP内存设置/限制。您可能能够找到指定("独立")GD内存限制的其他方法?

Edit:查看官方libgd常见问题解答,在接近底部的地方,它指出PHP扩展应该确实尊重内存限制,并且它指定8000x8000像素的图像将占用大约256MB,所以您的650MB限制应该真的足够了(除非您在同一运行中使用多个副本?)并且它不工作的事实证实了内存分配中发生了一些可疑的事情。

如果我是你,我会这样做:

  1. 发送消息到php-devel列表,看看这是否是一个已知的问题。你还需要搜索他们的bug追踪器。http://php.net/mailing-lists.php和https://bugs.php.net/
  2. 使用GD命令行工具,看看你是否可以处理相同的图像与相同的版本与cmd行东西。这是试图确定它是GD和图像的问题,还是PHP-gd库或某些组合的问题。

1。a创建一个PHP CLI程序来调整图像的大小,看看它是否有效

  • 弄清楚当你在底层(可能是c)代码中调用php函数时会发生什么。这将涉及到下载php-gd模块的源代码并查看它。编写一个最小的c应用程序,做与底层PHP-gd库相同的事情,看看是否可以重现错误
  • 里面的一些东西应该告诉你到底发生了什么,尽管这需要一些工作。您应该能够为您的环境获取所有工具/源代码。开源之美,对吧?

    另一种选择是在您的环境中尝试这些应用程序的不同版本,看看是否找到一个可以工作的版本。在我看来,这是一种"鸟枪式"的方法,并不是最好的方法。

    根据我的经验:

    • 尝试使用不同行大小的文件。我有另一个图像库的问题,没有读取图像长于3000px/行。否则,它不受绝对尺寸的限制。
    • 你有一个相当大的图像,如果这是在内存中的RGBA,你最终得到180M未压缩的图像数据,140M的RGB,仍然45M作为8Bit。您确定加载此文件时不会超过进程限制吗?

    您使用相同的图像与相同的库,这意味着libgdlibpng没有关于内存大小的限制(或至少不是一个您正在使用的大小),问题可能是php但您确定内存限制是650M(如果您的php安装使用suhosin,您可能要检查suhosin_memory_limit)

    你告诉内核正在杀死脚本(但我不知道你为什么这么说,有dmesg日志吗?)但是内核只在内存不足时才杀死进程。

    一个问题可能是内存碎片/连续分配,现在它更应该是内核空间问题,而不是用户空间问题,但是您的脚本分配的大小不是每天大小。如果您的脚本运行在具有长正常运行时间的服务器上,并且有许多进程分配和释放内存,那么碎片可能是一个问题,如果是您刚刚启动的虚拟机并且脚本在第一次启动时失败,那么问题不是碎片。

    你可以做一个简单的测试,命令和下面的代码,它使用gcc编译器创建(在你启动命令的目录中)一个a.out可执行文件,分配并设置为0大小为191 x 1024 x 1024的内存块(大约~190M)

    echo '#include <stdlib.h>'n#include <string.h>'n#define SIZ 191*1024*1024'nint main() {void *p=malloc(SIZ);memset(p,0,SIZ);return (p==NULL?1:0);}' '
    | gcc -O2 -xc -
    

    运行可执行文件:

    ./a.out
    

    如果问题是系统内存,内核应该杀死可执行文件(我不确定100%,内核有一些策略,当没有内存时杀死哪个进程,要检查)

    您应该看到kill消息,也许是dmesg

    (我认为这不应该发生,相反,malloc应该失败)

    如果malloc成功,可执行程序可以分配足够的内存,它应该返回0,如果它失败应该返回1

    验证返回代码只需使用

    ./a.out 
    echo $? 
    

    (中间不要执行其他命令)

    如果malloc失败,您可能只需要向系统添加物理或虚拟内存。

    请记住,~190M只是一个未压缩图像的内存,这取决于php, libgdlibpng如何工作,整个过程的内存甚至可能是两倍(或更多)。

    我做了一个简单的测试并分析了内存使用情况,在我的系统上,图像7600x2200(磁盘1.5M)的峰值约为~342M,下面是测试:

    <?php
    $im     = imagecreatefrompng("input.png");
    $string = "Sinker sucker socks pants";
    $orange = imagecolorallocate($im, 220, 210, 60);
    $px     = (imagesx($im) - 7.5 * strlen($string)) / 2;
    imagestring($im, 3, $px, 9, $string, $orange);
    imagepng($im);
    imagedestroy($im);
    

    我先试了xhprof,但是它返回的值太低了。

    所以我尝试了一个简单的脚本memusg

    memusg /usr/bin/php -f test.php >output.png
    

    (测试工作btw)

    我昨天遇到这个问题,今天修复了。

    昨天env:

    1. php-7.0.12
    2. libpng-1.6.26
    3. libgd-2.1.1

    当我调整png图像的大小时,这个套件会崩溃。内存检查后,我认为这可能是一个错误在最新的php或libpng。所以我把环境改成了:

    1. php-5.6.27
    2. libpng-1.2.56
    3. libgd-2.1.1

    我把php和libpng改成了长期使用的成熟版本。

    重新编译并重新安装这些-它可以很好地用于png和jpeg。