JDK-8020983 : OutOfMemoryError caused by non garbage collected JPEGImageWriter Instances
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 7u21
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_7
  • Submitted: 2013-05-03
  • Updated: 2014-11-17
  • Resolved: 2013-07-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7 JDK 8
5.0u55Fixed 6-poolResolved 7u40 b37Fixed 8Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
error with
java version  " 1.7.0_21 " 
and
java version  " 1.6.0_45 " 

No error with
java version  " 1.6.0_43 "  and prior


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
Red Hat: Linux 2.6.32-279.5.2.el6.x86_64
also reproducible on Mac OS

A DESCRIPTION OF THE PROBLEM :
When converting Images with
 " ImageWriter imageWriter = (ImageWriter) ImageIO.getImageWritersByFormatName( " jpeg " ).next(); "  You will receive an instance of JPEGImageWriter. Now call methods ?write? and ?flush? on this instance.

This image writer object will never be garbage collected. This is reproducible with java 6u45. It works fine for us until version 6_u43.

I checked also if it would work with newest Java 7 Release (Update 21) but it doesn't work neither.
Other Java 7 releases I have not tested!

Doing above description in a loop will cause the described OutOfMemoryError.
You can reproduce it with any heap sizes.
In my case I tried following
-Xms64m -Xmx128m
-Xms512m -Xmx1024m
-Xms512m -Xmx3072m
The error will always occur, but of course it takes longer as larger the heap is.

When creating a heap dump, you'll see that the number of  " living "  JPEGImageWriter instances and the number of saved images are equal

With other ImageWrtiters like  " gif "  or  " png "  the problem seems not to occur. Just the JPEGImageWriter is leaking

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
We have a complex application where one use case is to rescale images.
But I have written new sourcecode, that is much shorter than our real life application. But with the attached source code you can reproduce the problem on an easy level.

So just execute the attached java code (main method)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Exception in thread  " main "  java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.<init>(Unknown Source)
        at java.awt.image.Raster.createPackedRaster(Unknown Source)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Unknown Source)
        at java.awt.image.BufferedImage.<init>(Unknown Source)
        at OomTest.getImage(OomTest.java:62)
        at OomTest.main(OomTest.java:31)
ACTUAL -
Exception in thread  " main "  java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.<init>(Unknown Source)
        at java.awt.image.Raster.createPackedRaster(Unknown Source)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Unknown Source)
        at java.awt.image.BufferedImage.<init>(Unknown Source)
        at OomTest.getImage(OomTest.java:62)
        at OomTest.main(OomTest.java:31)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread  " main "  java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.<init>(Unknown Source)
        at java.awt.image.Raster.createPackedRaster(Unknown Source)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Unknown Source)
        at java.awt.image.BufferedImage.<init>(Unknown Source)
        at OomTest.getImage(OomTest.java:62)
        at OomTest.main(OomTest.java:31)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

public class OomTest {

  /**
   * The main method. Run it with  " little "  heap size (e.g java -Xms32m -Xmx64m OomTest) You will see it works fine
   * until jvm version 6_43, but crashes with 6_45 for {@link OutOfMemoryError} Also with bigger heap spaces it
   * crashes.
   *
   * When analyzing the heap dumps yo'll see that there are the same instances of ImageWriter as rounds run before
   * OutOfMemoryError occurred.
   *
   * @param args the arguments
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public static void main(String[] args) throws IOException {

    BufferedImage image;

    File file = new File( " output.jpg " );

    for (int i = 1; i < 1000; i++) {
      log( " read " , i);
      image = getImage();

      log( " getWriter " , i);
      ImageWriter imageWriter = (ImageWriter) ImageIO.getImageWritersByFormatName( " jpeg " ).next();

      ImageOutputStream ios = null;
      try {
        ios = ImageIO.createImageOutputStream(file);
        imageWriter.setOutput(ios);
        log( " write " , i);
        imageWriter.write(image);
        ios.flush();
      } finally {
        if (ios != null)
          ios.close();
      }
      System.out.println( " Congratulations: No OutOfMemoryError occurred. " );
    }
  }

  private static void log(String message, int repeat) {
    System.out.println(message +  "  round  "  + repeat);
    System.out.println( " totalMemory: "  + Runtime.getRuntime().totalMemory() / 1024 / 1024 +  " MB " );
    System.out.println( " maxMemory: "  + Runtime.getRuntime().maxMemory() / 1024 / 1024 +  " MB " );
    System.out.println( " freeMemory: "  + Runtime.getRuntime().freeMemory() / 1024 / 1024 +  " MB " );

  }

  private static BufferedImage getImage() {
    int width = 2500;
    int height = new Random().nextInt(2500) + 1;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    Graphics2D ig2 = image.createGraphics();
    ig2.fillRect(0, 0, width - 1, height - 1);

    return image;
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Stay on Java 6_43 not moving on, what is not acceptable.
Comments
Verified 7u40 b39 linux x64
15-08-2013

SQE ok to take the fix in 5u55.
10-08-2013

Moved the CPU13_04 critical watch label from backport bug to main.
06-08-2013

SQE is OK to take this
31-07-2013