Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 6745225
Votes 1
Synopsis Memory leak while drawing Attributed String
Category java:classes_2d
Reported Against
Release Fixed 7(b54)
State 10-Fix Delivered, bug
Priority: 3-Medium
Related Bugs
Submit Date 05-SEP-2008
Description
FULL PRODUCT VERSION :
The version i've done the most experimenting with is:
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b31)
Java HotSpot(TM) Server VM (build 14.0-b01, mixed mode)

but it seems to affect other java platforms as well, including openjdk 6

ADDITIONAL OS VERSION INFORMATION :
>uname -a
Linux me-laptop 2.6.24-19-generic #1 SMP Fri Jul 11 23:41:49 UTC 2008 i686 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
repeatedly creating images and drawing attributed strings in them leaks memory.  Eventually it causes a GC overhead limit exceeded exception.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
compile and run the class included in the "source code for an executable test case" section.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no data in this program needs to stick around; hence it should be able to run forever.
ACTUAL -
the memory footprint of the jvm slowly increases for about 10 minutes, topping off at nearly the jvm's limit (512 MB).  It continues to limp along for about another hour and a half, until an OutOfMemoryError is thrown during an allocation.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
GC overhead limit exceeded can be generated from many places--for example:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
        at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
        at java.awt.image.Raster.createPackedRaster(Raster.java:470)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
        at java.awt.GraphicsConfiguration.createCompatibleImage(GraphicsConfiguration.java:149)
        at vision.example.StringDrawMemoryLeak.leakMemory(StringDrawMemoryLeak.java:28)
        at vision.example.StringDrawMemoryLeak.main(StringDrawMemoryLeak.java:19)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.awt.font.TransformAttribute;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.text.AttributedString;

public class StringDrawMemoryLeak{
    
    public static final int imageSize = 20;
    
    public static void main(String[] args){
        while(true){
            leakMemory();
        }
    }
    
    public static void leakMemory(){
        
        //create an image
        GraphicsConfiguration g = GraphicsEnvironment.getLocalGraphicsEnvironment().
                                  getDefaultScreenDevice().getDefaultConfiguration();
        BufferedImage image = g.createCompatibleImage(imageSize, imageSize);
        Graphics2D g2 = image.createGraphics();
        
        //add a string with a bunch of transformations done to it
        AttributedString charStr = new AttributedString("A");
        AffineTransform trans = new AffineTransform();
        trans.rotate(Math.random()*.4*Math.PI - .2*Math.PI);
        trans.shear(Math.random()*.4-.2, Math.random()*.4-.2);
        charStr.addAttribute(TextAttribute.TRANSFORM, new TransformAttribute(trans));
        charStr.addAttribute(TextAttribute.SIZE, 10*Math.random()+10);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.drawString(charStr.getIterator(), 0, 0);
        
        //get the image data and dispose of the graphics
        image.getRGB(0, 0, imageSize, imageSize, null, 0, imageSize);
        g2.dispose();
    }
}
---------- END SOURCE ----------
Posted Date : 2008-09-05 11:37:06.0
Work Around
N/A
Evaluation
It seems that we are leaking AffineTransform objects. 
See attached screenshot from netbeans profiler.
Posted Date : 2008-09-05 16:19:22.0

We aren't really leaking AffineTransforms. We are just caching lots of them.
This is related to
6448405: static HashMap cache in LineBreakMeasurer can grow wihout bounds.
which improved matters, but seemingly did not completely cure it.

The fix for that changed the cache to be referenced via SoftReference.
Since some GC's will grow the heap to its maximum rather than clear
a SoftReference, then over time this will fill the heap, but GC
should clear this reference rather than throw out of memory exception.

Its possible that where this fails is if the objects that then
can be collected (are no longer strongly reachable), first need
some additional clean-up that prevents immediate storage recollection.

Another solution is a limit on the size of the cache, performed
by as simple a method as re-initialising it when it has more than
512 entries. In this case we don't really need a Reference at all.
Posted Date : 2008-12-23 21:54:44.0
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang