首页 > 解决方案 > 在 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));

我需要做些什么来启动垃圾收集吗?

感谢你的帮助!

标签: javaimage-processingcoldfusionbase64coldfusion-11

解决方案


您调用scale()了四次(每次调用createXXXThumbnail()),有效地使内存使用量翻了两番,因为您duplicate()通过参数显式传递了图像对象。ImageScaleToFit() 您还使用昂贵的默认插值四次调用本机,这可能会在内部创建另一个字节数组。因此,看起来无辜的 20 MB 在处理过程中可以轻松膨胀到 160 MB 甚至更多。


推荐阅读