java.lang.OutOfMemoryError: Java heap space when s

2020-07-22 19:48发布

问题:

I have 13255 images, each 240 x 240 pixels wide, the biggest 15,412 bytes in size and the smallest 839 bytes.

I am trying to loop through the folder adding each of them to a File[]. Once I have an array of each image I am then placing them inside a BufferedImage[] ready to be looped through and drawn onto a larger single image made up of each individual one.

Each image is named in the form of

Image x-y.png

However, I keep ending up with a java.lang.OutOfMemoryError: Java heap space error. I have no idea why. I have tried altering the size of the memory available to the JVM by adding parameters to the end of the target for Eclipse. Below is what I have used:

IDE's\eclipse-jee-juno-SR2-win32-x86_64\eclipse\eclipse.exe -vmargs -Xms64m -Xmx1024m

and

IDE's\eclipse-jee-juno-SR2-win32-x86_64\eclipse\eclipse.exe -vmargs -Xms64m -Xmx4096m

Both have no effect. I have also gone into Control Panel -> Programs -> Java and changed the amount of memory available there.

Here is the method that I have written :

public static void merge_images() throws IOException {
        int rows = 115; 
        int cols = 115;
        int chunks = rows * cols;

        System.out.println(chunks);

        int chunkWidth, chunkHeight;
        int type;
        // fetching image files
        File[] imgFiles = new File[chunks];

        int count = 0;
        for (int j = 1; j <= 115; j++) {
            for (int k = 1; k <= 115; k++) {
                imgFiles[count] = new File("G:\\Images\\Image " + j
                        + "-" + k + ".png");
                count++;
            }
        }
        System.out.println(imgFiles.length);

        // creating a buffered image array from image files
        BufferedImage[] buffImages = new BufferedImage[chunks];
        for (int i = 0; i < chunks; i++) {
            buffImages[i] = ImageIO.read(imgFiles[i]);
            System.out.println(i);
        }
        type = buffImages[0].getType();
        chunkWidth = buffImages[0].getWidth();
        chunkHeight = buffImages[0].getHeight();

        // Initializing the final image
        BufferedImage finalImg = new BufferedImage(chunkWidth * cols,
                chunkHeight * rows, type);

        int num = 0;
        for (int i = 0; i < rows; i++) {
            for (int k = 0; k < cols; k++) {
                finalImg.createGraphics().drawImage(buffImages[num], null,
                        chunkWidth * k, chunkHeight * i);
                num++;
            }
        }
        System.out.println("Image concatenated.....");
        ImageIO.write(finalImg, "png", new File("fusions.png"));
        System.out.println("Image Saved, Exiting");
    }

At the print line here

for (int i = 0; i < chunks; i++) {
            buffImages[i] = ImageIO.read(imgFiles[i]);
            System.out.println(i);
}

it always stops at around the 7320 point.

Here is the exact console print out

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.awt.image.DataBufferByte.<init>(Unknown Source)
    at java.awt.image.ComponentSampleModel.createDataBuffer(Unknown Source)
    at java.awt.image.Raster.createWritableRaster(Unknown Source)
    at javax.imageio.ImageTypeSpecifier.createBufferedImage(Unknown Source)
    at javax.imageio.ImageReader.getDestination(Unknown Source)
    at com.sun.imageio.plugins.png.PNGImageReader.readImage(Unknown Source)
    at com.sun.imageio.plugins.png.PNGImageReader.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at main.merge_images(main.java:48)
    at main.main(main.java:19)

Any ideas where I am going wrong would be greatly appreciated.

Regards,

Jamie

回答1:

You don't need to keep all your chunk images in memory. You can read them one by one and draw to your final image in final loop like this:

for (int i = 0; i < rows; i++) {
    for (int k = 0; k < cols; k++) {
        BufferedImage buffImage = ImageIO.read(imgFiles[num]);
        finalImg.createGraphics().drawImage(buffImage, null,
                chunkWidth * k, chunkHeight * i);
        num++;
    }
}

This will save at least half of the memory.



回答2:

You need to increase the maximum memory for your program, not the maximum of eclipse which doesn't need more memory I assume.

There is an option in eclipse for changing the VM arguments (I don't know that it is as I haven't used it for many years)

All the same you need to be able to load all the images uncompressed. This means you need at least 13225 * 240 * 240 * 4 bytes. This is just over 3 GB. If you load all the images first, you need at least double this. I suggest you make the heap at least 4 - 8 GB.



回答3:

Look like everyone is offering a simpler and less resource consuming solution. Let me try mine:

public static void merge_images() throws IOException {
    int rows = 115; 
    int cols = 115;

    System.out.println(rows * cols);

    // Initializing the final image
    BufferedImage finalImg = null;
    for (int i = 0; i < rows; i++) {
        for (int k = 0; k < cols; k++) {
            BufferedImage bufferedImage = ImageIO.read(new File("G:\\Images\\Image " + i + "-" + k + ".png"));
            int chunkWidth = bufferedImage.getWidth();
            int chunkHeight = bufferedImage.getHeight();
            if (finalImg == null) {
                finalImg = new BufferedImage(chunkWidth * cols, chunkHeight * rows, bufferedImage.getType());
            }
            finalImg.createGraphics().drawImage(bufferedImage, null, chunkWidth * k, chunkHeight * i);
        }
    }

    System.out.println("Image concatenated.....");
    ImageIO.write(finalImg, "png", new File("fusions.png"));
    System.out.println("Image Saved, Exiting");
}


回答4:

Try to run JVM with -Xmx1024m or the same with greater values. This value is the Heap size.

java -Xmx1024m -jar app.jar

Also, you can increase this value in your IDE.



回答5:

As was pointed out by hoaz (Many thanks) I was taking too many steps than were necessary. My solution is as follows:

public static void merge_images() throws IOException {
        int rows = 115; // we assume the no. of rows and cols are known and each
                        // chunk has equal width and height
        int cols = 115;
        int chunks = rows * cols;

        System.out.println(chunks);

        // fetching image files
        File[] imgFiles = new File[chunks];

        int count = 0;
        for (int j = 1; j <= 115; j++) {
            for (int k = 1; k <= 115; k++) {
                imgFiles[count] = new File("G:\\Images\\Image " + j
                        + "-" + k + ".png");
                count++;
            }
        }

        // Initializing the final image
        BufferedImage finalImg = new BufferedImage(240 * cols,
                240 * rows, 13);

        int num = 0;
        for (int i = 0; i < rows; i++) {
            for (int k = 0; k < cols; k++) {
                BufferedImage buffImage = ImageIO.read(imgFiles[num]);
                finalImg.createGraphics().drawImage(buffImage, null,
                        240 * k, 240 * i);
                num++;
            }
        }
        System.out.println("Image concatenated.....");
        ImageIO.write(finalImg, "png", new File("fusions.png"));
        System.out.println("Image Saved, Exiting");
    }

Essentially what I have done is remove the BufferedImage[] and instead created a new BufferedImage during the nested for loop when I am building up the final image. Rather than using variables I have hard coded the bounds of the images and type of each image.

Thanks for pointing me in the right direction hoaz.

Regards

Jamie