cache resources exhausted / 宽高超出限制
ImageMagick 的资源限制写在 policy.xml 里。如何查看当前限制、怎么安全地放宽,以及为什么真正的原因通常是 PDF 的 density 设得太大。
convert: cache resources exhausted 'input.jpg' @ error/cache.c/OpenPixelCache/4083
convert: width or height exceeds limit 'input.png' @ error/cache.c/OpenPixelCache/3742
发生了什么
ImageMagick 对内存、磁盘、面积和像素尺寸都设了上限,防止恶意构造的图片把主机资源吃干。和 PDF 那条封锁一样,这些限制也在 policy.xml 里,而且在一系列拒绝服务报告之后被收得相当紧。
查看当前限制
magick -list resource
Resource limits:
Width: 16KP Height: 16KP Area: 128MP
Memory: 256MiB Map: 512MiB Disk: 1GiB
Thread: 8 Time: unlimited
16KP 表示 16000 像素。任何一项超标都会失败。
放宽之前,先检查你的 density
最常见的触发原因并不是一张巨大的照片,而是转 PDF 时 density 和页面尺寸不匹配:
magick -density 600 poster.pdf out.png
A0 海报按 600 DPI 渲染大约是 8 万像素宽,接近 200 亿像素。这不是该去放宽的限制,是 density 本身就写错了。屏幕用途 150 完全够。density 怎么选见 PDF 那一页。
放宽限制
编辑 policy.xml(用 magick -list policy 找到它)里 domain="resource" 那几行:
<policy domain="resource" name="memory" value="4GiB"/>
<policy domain="resource" name="map" value="8GiB"/>
<policy domain="resource" name="disk" value="16GiB"/>
<policy domain="resource" name="area" value="512MP"/>
<policy domain="resource" name="width" value="64KP"/>
<policy domain="resource" name="height" value="64KP"/>
或者只对单次运行生效,不改任何文件
magick -limit memory 4GiB -limit map 8GiB -limit disk 16GiB \
input.tif -resize 2000 output.jpg
环境变量也可以,在 CI 里很方便:
export MAGICK_MEMORY_LIMIT=4GiB
export MAGICK_DISK_LIMIT=16GiB
export MAGICK_AREA_LIMIT=512MP
内存和磁盘限制是怎么配合的
ImageMagick 先把像素缓存放内存里,到达 memory 上限后改用内存映射文件(上限是 map),再超就退到普通磁盘(上限是 disk)。所以把 memory 设得很小、disk 设得很大并不会失败 —— 它只是变得极慢。如果一个本该几秒的任务跑了几分钟,多半就是在磁盘缓存上打转。
把 disk 设成 0 会让它直接失败而不是慢慢退化。在延迟可预测比「无论如何都要跑完」更重要的服务器上,这是个合理选择。
与其放宽限制,不如减少工作量
- 按缩小后的尺寸读取。 JPEG 可以直接按比例解码:
magick -define jpeg:size=2000x2000 huge.jpg -resize 1000 out.jpg,全程不会分配完整尺寸的图像。 - 用
-thumbnail。 它会先预缩放再做完整 resize,内存占用远小于-resize。 - 限制线程数。
-limit thread 2。每个线程都有自己的开销,在内存受限的容器里线程少反而更快。 - 逐页处理。 大的多页 PDF 请循环处理
input.pdf[n],不要一次载入全部页面。
在容器里
ImageMagick 读的是宿主机的内存,不是容器的 cgroup 限制,所以它会尝试申请超出容器允许的量,然后被 OOM 杀掉。在 Dockerfile 或启动脚本里显式设置这些限制,不要依赖默认值。