coobird / thumbnailator

Thumbnailator - a thumbnail generation library for Java
MIT License
5.14k stars 784 forks source link

java.lang.OutOfMemoryError: Java heap space when resize a specific image. #147

Closed textworld closed 2 years ago

textworld commented 4 years ago

Expected behavior

The error will not happen

Actual behavior

Got an OOM error.

Steps to reproduce the behavior

When I resize this image (https://1drv.ms/u/s!AlpawZKZPCw41DIr9maBJADZEfgS?e=e7TPce), the OOM error will happen. This image is a jpg file without the extension name.

public static final void main(String[] args) throws IOException {
        String file = "~/Desktop/2d6518b4d6b54756822a5b99714eeb6c64ae";
        String format = "jpg";
        int width = 100, height = 100;

        FileInputStream fileInputStream = new FileInputStream(file);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        Thumbnails.of(fileInputStream)
            .size(width, height)
            .outputFormat(format).outputQuality(1.0)
            .toOutputStream(byteArrayOutputStream); // this is the ImageDownloadController.java:110 in the trace info below.

        System.out.printf("OutputStream length: " + byteArrayOutputStream.size());
    }

And I got this

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:92)
    at java.awt.image.ComponentSampleModel.createDataBuffer(ComponentSampleModel.java:445)
    at java.awt.image.Raster.createWritableRaster(Raster.java:941)
    at javax.imageio.ImageTypeSpecifier.createBufferedImage(ImageTypeSpecifier.java:1074)
    at javax.imageio.ImageReader.getDestination(ImageReader.java:2892)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1082)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1050)
    at com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:412)
    at net.coobird.thumbnailator.tasks.io.InputStreamImageSource.read(Unknown Source)
    at net.coobird.thumbnailator.tasks.SourceSinkThumbnailTask.read(Unknown Source)
    at net.coobird.thumbnailator.Thumbnailator.createThumbnail(Unknown Source)
    at net.coobird.thumbnailator.Thumbnails$Builder.toOutputStream(Unknown Source)
    at com.enniu.cloud.services.sfsgateway.controller.ImageDownloadController.main(ImageDownloadController.java:110)

Environment

Please provide vendor and version information for the Operating System, JDK, and Thumbnailator. Please feel free to add any other information which may be pertinent.

textworld commented 4 years ago

And My vm options

-Xmn250m
-Xms500m
-Xmx500m
coobird commented 4 years ago

As you mentioned, you'll need to adjust your JVM heap size.

Thumbnailator needs to load the original image, which will require (roughly speaking) at least double of width * height * 4 bytes of heap space. For example a 20 megapixel image requires 160 MB of heap. This can be evidenced from the stack trace, which indicates that the OOM is occurring while reading the image:

    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1082)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1050)

Since the image you linked is a 15000 x 15000 image, this will require roughtly 900 MB of heap space just to load the image, and another 900 MB to resize the image for a total of 1800 MB of heap.

What you'll need to do is to adjust your JVM heap memory based on the expected image sizes that your application will be handling.

There is a temporary workaround (not guaranteed to always work) to reduce the necessary memory which can be enabled by adding the -Dthumbnailator.conserveMemoryWorkaround=true JVM option, but it can lead to reduction in the final thumbnail quality. More information can be found here.

coobird commented 2 years ago

Closing for no activity.