java - 在 ColdFusion 11 中使用 ImageReadBase64() 读取大图像时出现“java.lang.OutOfMemoryError:Java 堆空间”
问题描述
我在 ColdFusion 11 中内置了一个简单的 REST API,它允许我们的供应商更新产品信息。他们发送的大部分数据(大约 30 个属性)都是简单的值 - 字符串和数字。一个属性是表示图像文件的 Base64 编码字符串。REST API 更新数据库中的简单数据值,然后读取图像并将其调整为适合在网站上使用的 4 种大小。我注意到供应商偶尔会发送带有一些请求 (20MB+) 的大图像,并且这些请求通常会导致“java.lang.OutOfMemoryError: Java heap space”出现在ImageReadBase64()
函数被调用。我们在此服务器上运行了其他 Web 应用程序,似乎处理这些图像时被锁定的内存也会导致这些应用程序出现问题。我试图弄清楚我是否可以在代码中做任何事情来减少这些问题,即使它正在想办法在图像“太大”(超过一定大小)时不处理图像。以下是为处理图像而运行的所有代码:
public struct function getDimensionsToEnlarge(
required numeric imageWidth,
required numeric imageHeight,
required numeric minWidth,
required numeric minHeight
) {
LOCAL.Dimensions={
width=-1,
height=-1
};
if ( ARGUMENTS.minHeight > 0
&& ARGUMENTS.minWidth > 0
&& ARGUMENTS.imageHeight < ARGUMENTS.minHeight
&& ARGUMENTS.imageWidth < ARGUMENTS.minWidth
) {
LOCAL.Dimensions.width=ARGUMENTS.minWidth;
LOCAL.Dimensions.height=ARGUMENTS.minHeight;
}
return LOCAL.Dimensions;
}
public struct function getDimensionsToShrink(
required numeric imageWidth,
required numeric imageHeight,
required numeric maxWidth,
required numeric maxHeight
) {
LOCAL.Dimensions={
width=-1,
height=-1
};
if ( ARGUMENTS.maxHeight > 0
&& ARGUMENTS.maxWidth > 0
&& ( ARGUMENTS.imageHeight > ARGUMENTS.maxHeight
|| ARGUMENTS.imageWidth > ARGUMENTS.maxWidth
)
) {
LOCAL.Dimensions.width=ARGUMENTS.maxWidth;
LOCAL.Dimensions.height=ARGUMENTS.maxHeight;
}
return LOCAL.Dimensions;
}
public struct function getDimensionsToFit(
required numeric imageWidth,
required numeric imageHeight,
required numeric minWidth,
required numeric minHeight,
required numeric maxWidth,
required numeric maxHeight
) {
LOCAL.Dimensions={
width=-1,
height=-1
};
LOCAL.Dimensions =
getDimensionsToEnlarge(
imageHeight=ARGUMENTS.imageHeight,
imageWidth=ARGUMENTS.imageWidth,
minWidth=ARGUMENTS.minWidth,
minHeight=ARGUMENTS.minHeight
);
if (LOCAL.Dimensions.width < 0 && LOCAL.Dimensions.height < 0)
LOCAL.Dimensions =
getDimensionsToShrink(
imageHeight=ARGUMENTS.imageHeight,
imageWidth=ARGUMENTS.imageWidth,
maxWidth=ARGUMENTS.maxWidth,
maxHeight=ARGUMENTS.maxHeight
);
return LOCAL.Dimensions;
}
public any function scale(
required any image,
string action="fit",
numeric minWidth=-1,
numeric minHeight=-1,
numeric maxWidth=-1,
numeric maxHeight=-1
) {
LOCAL.Dimensions={
width=-1,
height=-1
};
LOCAL.Image=Duplicate(ARGUMENTS.image);
switch (ARGUMENTS.action) {
case "shrink":
LOCAL.Dimensions =
getDimensionsToShrink(
imageHeight=LOCAL.Image.getHeight(),
imageWidth=LOCAL.Image.getWidth(),
maxWidth=ARGUMENTS.maxWidth,
maxHeight=ARGUMENTS.maxHeight
);
break;
case "enlarge":
LOCAL.Dimensions =
getDimensionsToEnlarge(
imageHeight=LOCAL.Image.getHeight(),
imageWidth=LOCAL.Image.getWidth(),
minWidth=ARGUMENTS.minWidth,
minHeight=ARGUMENTS.minHeight
);
break;
default:
LOCAL.Dimensions =
getDimensionsToFit(
imageHeight=LOCAL.Image.getHeight(),
imageWidth=LOCAL.Image.getWidth(),
minWidth=ARGUMENTS.minWidth,
minHeight=ARGUMENTS.minHeight,
maxWidth=ARGUMENTS.maxWidth,
maxHeight=ARGUMENTS.maxHeight
);
break;
}
if (LOCAL.Dimensions.width > 0 && LOCAL.Dimensions.height > 0) {
ImageSetAntialiasing(LOCAL.Image, "on");
ImageScaleToFit(
LOCAL.Image,
LOCAL.Dimensions.width,
LOCAL.Dimensions.height
);
}
return LOCAL.Image;
}
public void function createLargeThumbnail(required any image) {
ImageWrite(
scale(image=ARGUMENTS.image, maxHeight=500, maxWidth=700),
getLargeImagePath(),
0.75,
true
);
return;
}
public void function createMediumThumbnail(required any image) {
ImageWrite(
scale(image=ARGUMENTS.image, maxHeight=300, maxWidth=300),
getMediumImagePath(),
0.75,
true
);
return;
}
public void function createSmallThumbnail(required any image) {
ImageWrite(
scale(image=ARGUMENTS.image, maxHeight=50, maxWidth=50),
getSmallImagePath(),
0.75,
true
);
return;
}
public void function createMobileThumbnail(required any image) {
ImageWrite(
scale(image=ARGUMENTS.image, maxHeight=50, maxWidth=300),
getMobileImagePath(),
0.75,
true
);
return;
}
public void function createProductThumbnails(required any image) {
createLargeThumbnail(argumentCollection=ARGUMENTS);
createMediumThumbnail(argumentCollection=ARGUMENTS);
createSmallThumbnail(argumentCollection=ARGUMENTS);
createMobileThumbnail(argumentCollection=ARGUMENTS);
return;
}
createProductThumbnails(ImageReadBase64(ARGUMENTS.ImageUpload));
我需要做些什么来启动垃圾收集吗?
感谢你的帮助!
解决方案
您调用scale()
了四次(每次调用createXXXThumbnail()
),有效地使内存使用量翻了两番,因为您duplicate()
通过参数显式传递了图像对象。ImageScaleToFit()
您还使用昂贵的默认插值四次调用本机,这可能会在内部创建另一个字节数组。因此,看起来无辜的 20 MB 在处理过程中可以轻松膨胀到 160 MB 甚至更多。