我有一个广泛使用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限制应该真的足够了(除非您在同一运行中使用多个副本?)并且它不工作的事实证实了内存分配中发生了一些可疑的事情。
如果我是你,我会这样做:
- 发送消息到php-devel列表,看看这是否是一个已知的问题。你还需要搜索他们的bug追踪器。http://php.net/mailing-lists.php和https://bugs.php.net/
- 使用GD命令行工具,看看你是否可以处理相同的图像与相同的版本与cmd行东西。这是试图确定它是GD和图像的问题,还是PHP-gd库或某些组合的问题。
1。a创建一个PHP CLI程序来调整图像的大小,看看它是否有效
- 弄清楚当你在底层(可能是c)代码中调用php函数时会发生什么。这将涉及到下载php-gd模块的源代码并查看它。编写一个最小的c应用程序,做与底层PHP-gd库相同的事情,看看是否可以重现错误
里面的一些东西应该告诉你到底发生了什么,尽管这需要一些工作。您应该能够为您的环境获取所有工具/源代码。开源之美,对吧?
另一种选择是在您的环境中尝试这些应用程序的不同版本,看看是否找到一个可以工作的版本。在我看来,这是一种"鸟枪式"的方法,并不是最好的方法。
根据我的经验:
- 尝试使用不同行大小的文件。我有另一个图像库的问题,没有读取图像长于3000px/行。否则,它不受绝对尺寸的限制。
- 你有一个相当大的图像,如果这是在内存中的RGBA,你最终得到180M未压缩的图像数据,140M的RGB,仍然45M作为8Bit。您确定加载此文件时不会超过进程限制吗?
您使用相同的图像与相同的库,这意味着libgd
和libpng
没有关于内存大小的限制(或至少不是一个您正在使用的大小),问题可能是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
, libgd
和libpng
如何工作,整个过程的内存甚至可能是两倍(或更多)。
我做了一个简单的测试并分析了内存使用情况,在我的系统上,图像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:- php-7.0.12
- libpng-1.6.26
- libgd-2.1.1
当我调整png图像的大小时,这个套件会崩溃。内存检查后,我认为这可能是一个错误在最新的php或libpng。所以我把环境改成了:
- php-5.6.27
- libpng-1.2.56
- libgd-2.1.1
我把php和libpng改成了长期使用的成熟版本。
重新编译并重新安装这些-它可以很好地用于png和jpeg。