gd-png无法分配图像数据

时间:2015-08-05 23:21:43

标签: php gd

我有一个旧的代码库,广泛使用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,足够的内存分配。

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

5 个答案:

答案 0 :(得分:4)

我正在浏览PHP 5.5.9源代码,试图找到你的特定错误字符串“无法分配图像数据”,但我能找到的最接近的是“gd-png错误:无法分配gdImage结构”。捆绑版本的PHP for PHP 5.5.9(一直到7.0.0)是版本2.0.35,所以看起来你正在看一个自定义构建(?)。窃听PHP人员可能没有帮助。

看GD源(2.1.2,似乎找不到2.1.1), only 这个错误发生的地方是: 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开发人员联系?。为了避免快乐的追逐,我认为尝试另一种环境是最好的选择 - 确认它们确实使用非straard GD版本。

在更多的PHP源代码之后,似乎safe_emalloc在所有扩展中都被使用,所以我猜(猜想你)这是为PHP扩展(looks like it)分配内存的首选方法。如果您的环境实际上使用的是未更改的GD 2.1.1,则可能忽略了任何PHP内存设置/限制。您可能能够找到另一种指定('独立')GD内存限制的方法吗?

编辑:查看底部附近的official libgd faq,它表明PHP扩展确实尊重内存限制,并指定8000x8000像素图像需要大约256MB,所以你的650MB限制应该足够了(除非你在同一次运行中使用多个副本?)并且它不起作用的事实证实了内存分配正在发生的事情。

答案 1 :(得分:3)

如果我是你,我会做以下事情:

  1. 将消息发送到php-devel列表,看看它是否是一个已知问题。您还需要搜索他们的错误跟踪器。 http://php.net/mailing-lists.phphttps://bugs.php.net/
  2. 使用GD命令行实用程序查看是否可以使用cmd line stuff处理相同版本的相同图像。这是试图确定它是GD和图像的问题,还是PHP-gd lib或某种组合的问题。
  3. 1.a创建一个PHP CLI程序来调整图像大小并查看它是否有效

    1. 弄清楚在底层(可能是c)代码中调用php函数时会发生什么。这将涉及将源代码下载到php-gd模块并查看它。
    2. 编写一个与底层PHP-gd库完全相同的最小c应用程序,看看是否可以重现错误
    3. 那里的东西应该告诉你究竟发生了什么,虽然它需要一些工作。您应该能够获得适合您环境的所有工具/来源。开源之美吧?

      另一种选择是在您的环境中尝试这些应用程序的不同版本,看看是否找到了可行的应用程序。这是“霰弹枪”的方法而不是最好的IMO。

答案 2 :(得分:0)

根据我的经验:

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

答案 3 :(得分:0)

您使用相同的图片使用相同的图片,这意味着libgdlibpng对内存大小没有限制(或者至少不是您使用的尺寸的限制),问题可能是 php ,但您确定内存限制为650M(如果您的 php 安装使用suhosin,您可能需要检查suhosin.memory_limit

你告诉内核正在杀死脚本(但是我无法找到你这么说的原因,是否有dmesg日志?)但内核只在内存不足时终止进程

一个问题可能是内存碎片 / 连续的alloc ,现在它应该比用户空间<更多内核空间问题/ em>,但脚本分配的大小不是每天大小。 如果您的脚本在运行时间很长且具有大量进程分配和释放内存的服务器上运行,如果您刚刚启动并且脚本在首次启动时失败,那么问题就不会出现问题。 #39; t fragmentation。

您可以在下面执行一个简单的测试,命令和代码,它使用gcc编译器(在您启动命令的目录中)创建a.out可执行文件,该文件分配并设置为0 a内存块大小为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成功,可执行文件可以分配足够的内存,它应该返回0,如果失败应该返回1

验证返回代码只是使用

malloc

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

如果./a.out echo $? 失败,您可能只需要向系统添加物理或虚拟内存。

请记住,~190M只是一个未压缩图像的内存,具体取决于mallocphplibgd的工作方式,整个过程的内存可能是两次(或更多)。

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

libpng

我最初尝试过xhprof但它返回的值太低了。

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

<?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);

(测试工作顺便说一句)

答案 4 :(得分:0)

我昨天遇到了这个问题并立即修复了。

昨天的环境:

  1. PHP-7.0.12
  2. 的libpng-26年6月1日
  3. 的libgd-2.1.1
  4. 调整png图像大小时,此套件会崩溃。 内存检查后,我认为它可能是最新的php或libpng中的一个错误。 所以我把env更改为:

    1. PHP-27年6月5日
    2. 的libpng-56年1月2日
    3. 的libgd-2.1.1
    4. 我将php和libpng更改为长期使用的成熟版本。

      重新编译并重新安装 - 它适用于png和jpeg。