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-devel 列表,看看它是否是一个已知问题。您还需要搜索他们的错误跟踪器。 http://php.net/mailing-lists.php and https://bugs.php.net/
- 使用 GD 命令行实用程序查看是否可以使用 cmd 行东西处理具有相同版本的相同图像。这是试图确定它是否是 GD 和图像的问题,或者它是否是 PHP-gd 库或某些组合的问题。
1.a 创建一个 PHP CLI 程序来调整图像大小并查看它是否有效
- 找出在底层(可能是 c)代码中调用 php 函数时究竟发生了什么。这将涉及将源代码下载到 php-gd 模块并浏览它。
- 编写一个与底层 PHP-gd 库做同样事情的最小 c 应用程序,看看是否可以重现错误
其中的内容应该会准确告诉您发生了什么,尽管这需要一些工作。您应该能够为您的环境获取所有 tools/sources。开源之美,对吧?
另一种选择是在您的环境中尝试这些应用程序的不同版本,看看是否能找到一个有效的版本。这是 "shotgun" 方法而不是最好的方法,IMO。
根据我的经验:
- 尝试使用不同行大小的文件。我遇到了另一个图像库的问题,该图像库读取的图像长度不超过 3000 像素/行。它在其他方面不受绝对大小的限制。
- 你那里有很多图像,如果它在内存中的 RGBA 中,你最终会得到 180M 未压缩图像数据,140M 在 RGB 上,仍然是 45M 作为 8 位。您确定加载时不会超出您的进程限制?
我浏览了 PHP 5.5.9 源代码,试图找到您的特定错误字符串 "cannot allocate image data",但我能找到的最接近的是 "gd-png error: cannot allocate gdImage struct"。 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 用于所有扩展,所以我猜想(请注意)这是为 [=52] 分配内存的首选方式=] 扩展(looks like it)。如果您的环境实际上使用的是未更改的 GD 2.1.1,则它可能会忽略任何 PHP 内存 settings/limits。您也许可以找到其他指定 ('stand-alone') GD 内存限制的方法?
编辑: 查看 official libgd faq,在底部附近它指出 PHP 扩展 应该 确实尊重内存限制,它指定一个 8000x8000 像素的图像大约需要 256MB,所以你的 650MB 限制应该足够了(除非你在同一个 运行 中处理多个副本?)不工作证实内存分配有问题。
您使用相同的图像和相同的库,这意味着 libgd
和 libpng
对内存大小没有限制(或者至少对您使用的大小没有限制),问题可能是 php 但您确定内存限制是 650M
(如果您的 php安装正在使用 suhosin
,您可能需要检查 suhosin.memory_limit)
你告诉内核正在终止脚本(但我找不到你为什么这么说,是否有关于 dmesg
的日志?)但内核仅在内存不足时终止进程.
一个问题可能是内存碎片/连续分配,现在它应该更像是一个内核space 问题比 user space,但您的脚本分配的大小不是 每天 大小。
如果您的脚本 运行 在正常运行时间很长且有大量进程分配和释放内存的服务器上运行,如果是您刚刚启动的虚拟机并且脚本在第一次启动时失败,则碎片可能是一个问题, 那么问题就不是碎片化了。
您可以在下面做一个简单的测试、命令和代码,它使用 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%,内核有一些关于当没有内存时终止哪个进程的策略,待检查)
不过您应该会看到终止消息,也许 dmesg
。
(我认为这不应该发生,相反 malloc
应该会失败)
如果malloc
成功,可执行文件可以分配足够的内存,它应该return0,如果失败应该return1
验证 return 代码只需使用
./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,但是 returned 值太低了。
所以我尝试了一个简单的脚本memusg
memusg /usr/bin/php -f test.php >output.png
(测试工作顺便说一句)
昨天遇到这个问题,今天解决了。
昨天的环境:
- 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。
我有一个广泛使用 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-devel 列表,看看它是否是一个已知问题。您还需要搜索他们的错误跟踪器。 http://php.net/mailing-lists.php and https://bugs.php.net/
- 使用 GD 命令行实用程序查看是否可以使用 cmd 行东西处理具有相同版本的相同图像。这是试图确定它是否是 GD 和图像的问题,或者它是否是 PHP-gd 库或某些组合的问题。
1.a 创建一个 PHP CLI 程序来调整图像大小并查看它是否有效
- 找出在底层(可能是 c)代码中调用 php 函数时究竟发生了什么。这将涉及将源代码下载到 php-gd 模块并浏览它。
- 编写一个与底层 PHP-gd 库做同样事情的最小 c 应用程序,看看是否可以重现错误
其中的内容应该会准确告诉您发生了什么,尽管这需要一些工作。您应该能够为您的环境获取所有 tools/sources。开源之美,对吧?
另一种选择是在您的环境中尝试这些应用程序的不同版本,看看是否能找到一个有效的版本。这是 "shotgun" 方法而不是最好的方法,IMO。
根据我的经验:
- 尝试使用不同行大小的文件。我遇到了另一个图像库的问题,该图像库读取的图像长度不超过 3000 像素/行。它在其他方面不受绝对大小的限制。
- 你那里有很多图像,如果它在内存中的 RGBA 中,你最终会得到 180M 未压缩图像数据,140M 在 RGB 上,仍然是 45M 作为 8 位。您确定加载时不会超出您的进程限制?
我浏览了 PHP 5.5.9 源代码,试图找到您的特定错误字符串 "cannot allocate image data",但我能找到的最接近的是 "gd-png error: cannot allocate gdImage struct"。 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 用于所有扩展,所以我猜想(请注意)这是为 [=52] 分配内存的首选方式=] 扩展(looks like it)。如果您的环境实际上使用的是未更改的 GD 2.1.1,则它可能会忽略任何 PHP 内存 settings/limits。您也许可以找到其他指定 ('stand-alone') GD 内存限制的方法?
编辑: 查看 official libgd faq,在底部附近它指出 PHP 扩展 应该 确实尊重内存限制,它指定一个 8000x8000 像素的图像大约需要 256MB,所以你的 650MB 限制应该足够了(除非你在同一个 运行 中处理多个副本?)不工作证实内存分配有问题。
您使用相同的图像和相同的库,这意味着 libgd
和 libpng
对内存大小没有限制(或者至少对您使用的大小没有限制),问题可能是 php 但您确定内存限制是 650M
(如果您的 php安装正在使用 suhosin
,您可能需要检查 suhosin.memory_limit)
你告诉内核正在终止脚本(但我找不到你为什么这么说,是否有关于 dmesg
的日志?)但内核仅在内存不足时终止进程.
一个问题可能是内存碎片/连续分配,现在它应该更像是一个内核space 问题比 user space,但您的脚本分配的大小不是 每天 大小。 如果您的脚本 运行 在正常运行时间很长且有大量进程分配和释放内存的服务器上运行,如果是您刚刚启动的虚拟机并且脚本在第一次启动时失败,则碎片可能是一个问题, 那么问题就不是碎片化了。
您可以在下面做一个简单的测试、命令和代码,它使用 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%,内核有一些关于当没有内存时终止哪个进程的策略,待检查)
不过您应该会看到终止消息,也许 dmesg
。
(我认为这不应该发生,相反 malloc
应该会失败)
如果malloc
成功,可执行文件可以分配足够的内存,它应该return0,如果失败应该return1
验证 return 代码只需使用
./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,但是 returned 值太低了。
所以我尝试了一个简单的脚本memusg
memusg /usr/bin/php -f test.php >output.png
(测试工作顺便说一句)
昨天遇到这个问题,今天解决了。
昨天的环境:
- 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。