United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6716560 BufferedImage scaling leaks memory
JDK-6716560 : BufferedImage scaling leaks memory

Details
Type:
Bug
Submit Date:
2008-06-19
Status:
Open
Updated Date:
2012-01-16
Project Name:
JDK
Resolved Date:
Component:
client-libs
OS:
windows_xp
Sub-Component:
2d
CPU:
x86
Priority:
P4
Resolution:
Unresolved
Affected Versions:
5.0
Targeted Versions:

Related Reports

Sub Tasks

Description
FULL PRODUCT VERSION :
jdk 1.5.0_12

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600] SP3

A DESCRIPTION OF THE PROBLEM :
Scaling a BufferedImage instance (by JAI, by g.drawRenderedImage with an AffineTransform, by g.drawImage with g.setScale -- does not matter) consumes an exorbitant amount of memory and does not free that memory.

As a concrete example, supplied in the zip file cited in Steps to Reproduce, a bit map image of size 1750x2250 scaled 1.34x consumes some 40+Mb of memory and does not free the memory after completion of the scaling.

IMO, this is a maximum severity bug as, short of implementing one's own scaling (at a performance hit), there is no way to acceptably handle otherwise seemingly reasonable BI scaling instances. (For example, hoping to scale a 480kb bitmap image like the example image to 4x).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test case class available in this zip with the tiff (also in the zip):
http://www.goedel.ch/bugs/java/NoUIZoom.zip

for example:
java bi_memory_one.NoUIZoom -Xmx128m 1750x2250_unc_bitmap.tif 0.67 GRAPHICS_DRAW_SCALE_IMAGE


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Scaling a 480kb tiff by 1.34x does not require 55Mb of memory (and if it does for some reason, that memory is free'd after the scaling operation completes).

ACTUAL -
Memory usage grows like this:
The image loaded should consume about 480Kb of memory (bitsPerPixel * imageWidth * imageHeight).
	Used JVM memory at this point is somewhere around 1.3Mb. Ok.
The first scale to 0.67 the original size is performed. This scaledImage BufferedImage should consume about about 0.21Mb of memory.
	Used JVM memory is somewhere around 14.7Mb now.
The second scale to 1.34 the original size is performed - but not before the scaledImage is disposed of in every conceivable way (and gc() is invoked just to be ridiculous). The new scaledImage should consume about 0.84 megs.
	Used JVM memory is somewhere around 55.7Mb now -- the wings are definitely off the plane.
The third scale is attempted to be made to 2.68x the original size. This should produce a whopping 3.3Mb BufferedImage - but we're out of heap space somehow.

Output from the test case class, using the arguments suggested in the "Steps to Reproduce" dumps:
	Scaling image [C:\Documents and Settings\xxxx\Desktop\1750x2250_unc_bitmap.tif]
		Using scaling type: GRAPHICS_DRAW_SCALE_IMAGE
		total available heap size: 127.06 megs (133234688 bytes)
	-------
	--> loaded BI (memory byte size: 0.47 megs (492187 bytes)) [usedMem: 1.29 megs (1351232 bytes)]
		about to start first scaling [usedMem: 1.29 megs (1351232 bytes)] -->
	--> 	buffered image created [usedMem: 1.47 megs (1536752 bytes)]
				about to render scaling by 0.67x [usedMem: 1.47 megs (1540800 bytes)] -->
	--> first scaling finished - byte size of the new image should be 0.21 megs (220775 bytes) [usedMem: 14.67 megs (15380728 bytes)]
		about to start second scaling [usedMem: 14.67 megs (15380728 bytes)] -->
	--> 	buffered image created [usedMem: 1.74 megs (1826840 bytes)]
				about to render scaling by 1.34x [usedMem: 1.74 megs (1826840 bytes)] -->
	--> second scaling finished - byte size of the new image should be 0.84 megs (883771 bytes) [usedMem: 55.7 megs (58405536 bytes)]
		about to start third scaling [usedMem: 55.7 megs (58405536 bytes)] -->
	--> 	buffered image created [usedMem: 5.13 megs (5383480 bytes)]
				about to render scaling by 2.68x [usedMem: 5.13 megs (5383480 bytes)] -->
	Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
		at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)
		...
		at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2835)
		at bi_memory_one.NoUIZoom.scaleImageViaGraphicsDrawScaleImage(NoUIZoom.java:210)
		at bi_memory_one.NoUIZoom.scaleImage(NoUIZoom.java:120)
		at bi_memory_one.NoUIZoom.runTheTestScenarioAndExit(NoUIZoom.java:294)
		at bi_memory_one.NoUIZoom.main(NoUIZoom.java:362)
-


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
Class (and tiff, though this happens with all BufferedImage data, regardless of source type) are here:
http://www.goedel.ch/bugs/java/NoUIZoom.zip

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

                                    

Comments
EVALUATION

The OOM is because of allocation of intermediate helper image by the
draw image pipeline. There is no memory leak here as this intermediate
image is freed after completion of transform.
 
To handle generic case intermediate image is of type INT_ARGB and for
given example we will be trying to allocate intermediate image with
dimensions 4690x6030 (approximately 113Mb) which doesn't fit into the
available heap (total heap is 128Mb).

Intermediate image is needed for several reasons. E.g. to handle
interpolation properly.

However, current memory overhead is too high and clearly it should
be possible to reduce it for cases like given example. We should
investigate ways to bound memory overhead for handling arbitrary
transformations (e.g. by using fixed size intermediate buffers)
but we also need to make sure it will not hurt performance much.
                                     
2009-03-18



Hardware and Software, Engineered to Work Together