|
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
|
|
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
|