Reputation: 8310
If the specified image file contains an image too large, in term of required memory to load it, an OutOfMemoryError
is raised. For the same reason, if the scale factor of a scaling operation would create a too large image, an OutOfMemoryError
is raised as follows:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
at java.awt.image.Raster.createPackedRaster(Raster.java:467)
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
at java.awt.image.BufferedImage.<init>(BufferedImage.java:340)
at java.awt.image.AffineTransformOp.createCompatibleDestImage(AffineTransformOp.java:461)
at java.awt.image.AffineTransformOp.filter(AffineTransformOp.java:226)
Is it be possible to prevent this error from occurring, by performing some preliminary check? For example, if the application determines the maximum image size (in pixels) that can be loaded into memory by the amount of available memory, you could avoid performing scaling, whose resulting image would have a number of pixels greater than the determined limit.
How to handle the above problem with an image that is to be read from file? How to get the size of an image file without load it in memory? This problem could be handled using the solutions proposed in this Q&A: this solution is very fast bacause the image size is read from the file, without loading the whole image.
In summary...
The first target consists in performing a preliminary check in order to get the size of the image to show, without load the whole image:
In both cases, you get the size of the image that should be loaded into memory, ie the total number of pixels. This value could be compared to a value limit. But, how to calculate this value limit? How to determine the maximum image size or the maximum number of pixels that can be loaded into memory by the amount of available memory?
Upvotes: 1
Views: 921
Reputation: 27094
As you seem to already have found out, from looking at the linked answers and comments, you can obtain the ImageReader
for a given format, then ask the reader for dimensions of the final image. However, even if the VM has enough free memory, keep in mind that a BufferedImage
needs a contiguous block of memory, and that the largest block available may be a lot smaller than the available memory, due to fragmentation. AFAIK there's no API to find the size of the "largest continuous block of memory" from a VM.
It is possible to create the image up front, before attempting to decode, by using code like this (simplified for readability):
ImageReader reader; // the reader for your format
reader.setInput(input); // your input
// Create image from type specifier
ImageTypeSpecifier spec = reader.getImageTypes(0).next();
BufferedImage destination = spec.createBufferedImage(reader.getWidth(), reader.getHeight();
ImageReadParam param = reader.getDefaultReadParam();
param.setDestination(destination);
reader.read(0, param); // Image will be decoded to destination
However, keep in mind that the decoding process may need a bit of extra memory for data structures, buffers etc., to efficiently decode images. So even if you know the dimensions, or even have the destination image in memory, there's still a possibility that you will run out of memory while decoding.
(Both creating a destination image up front and the memory used by the process also applies to AffineTransformOp
or any BufferedImageOp
, really).
So, I think I'll second @skykings advice, you should eventually handle the OutOfMemoryError
in some way. It's virtually impossible to guarantee that you have enough memory to decode a given image up front.
Upvotes: 4