United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6209673 : Memory leak in Swing applications after a display mode change

Details
Type:
Bug
Submit Date:
2004-12-16
Status:
Resolved
Updated Date:
2010-04-02
Project Name:
JDK
Resolved Date:
2006-08-02
Component:
client-libs
OS:
windows_2003,windows_xp
Sub-Component:
javax.swing
CPU:
x86
Priority:
P2
Resolution:
Fixed
Affected Versions:
1.4.2,5.0
Fixed Versions:

Related Reports
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode)

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

EXTRA RELEVANT SYSTEM CONFIGURATION :
nVidia Geforce4 Ti 4200 graphics card.  nVidia drivers version 6.14.10.6177

A DESCRIPTION OF THE PROBLEM :
I have noticed that very basic drawing in a Swing application will eventually lead to a memory leak in the AWT Event thread that prevents re-painting.  All that is needed to demonstrate this is a Swing app that repaints the UI periodically.  I used a JFrame with a single JLabel and called repaint() ten times a second.
The tricky bit about the bug, is that it is triggered by a completely different process, in my case a fancy screen blanker that I believe uses OpenGL.   If I run the described Swing app and activate the GoldFish Aquarium screen saver from www.lifeglobe.com the swing application will lose a chunk of memory.  Simply repeatedly activating and deactivating the screen saver is enough to crash the Java application after only a few iterations.
The bug can be seen in Java 1.4.2_05 and Java 5.
I have only been able to reproduce this bug with that specific screen saver, only when the screen saver is set to 16 bpp and my desktop is set to 32 bpp.  Presumably the 3D screen savers included with windows (e.g. 3D Pipes) don't change the bit depth, or use Direct3D instead of openGL or vice versa, They don't appear to trigger the problem.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Install the Goldfish Aquarium screen saver from www.lifeglobe.com
Set your desktop resolution to 1920x1440 32bpp
Make sure the screen saver is configured for 16bpp
Compile and run the attached Java program from a console with no options (java GraphicLeak).
Repeatedly activate and de-activate the screen saver while watching memory usage of the java process with the Windows task manager.
Eventually you will see OutOfMemoryExceptions reported in the console window where you started the Java application.
For me it happens after activating the screen saver for the seventh time.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The java application should not lose memory when another process makes use of the graphics card.
ACTUAL -
Each invocation of the screen saver causes a memory loss in the java application.  The memory loss may be proportional to the size of the java application's UI.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
//
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class GraphicLeak
{
    public static void main(String[] args)
    {
        JFrame frm = new JFrame("Memory leak");
        Container cp = frm.getContentPane();
	cp.add( new JLabel(
            "<html>Launch Windows Task Manager<br>Maximize the size fo this window.<br>"+
            "Watch the memory consumption of this process.<br>"+
            "See that it is in a steady state.<br>"+
            "Activate the \"Goldfish Aquarium\" screensaver<br>"+
            "(free trial www.lifeglobe.com 16bpp, default res. of 1920 x1440, limit framerate)<br>"+
            "Wait a few seconds minutes, stop the screen saver and look at the memory usage of this process.<br>"+
            "Repeat activating the screen saver and deactivating it, several times.<br>"+
            "Memory usage of this process appears to jump every time the screen saver starts and stops.<br>"+
            "If left long enough Out-of-memory errors will occur with every reapint.<br>"+
            "</html>"), BorderLayout.CENTER );

        frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);

        while(true)
        {
            frm.repaint();
            try { Thread.sleep(100); } catch (InterruptedException e ) {}
        }
    }
}
---------- END SOURCE ----------
###@###.### 2004-12-16 19:36:05 GMT

This bug is a manifestation of an even simpler situation where _any_ display mode that occurs outside of the Java application (including any screen saver activating, or hibernating the system, or another app changing the display mode, or the user changing the display mode via the control panel) will cause the java application to leak memory.  If you want to reproduce the problem, run the app above (or any Swing application, actually) and change the color depth of the screen using the Display Control Panel.  Bring up the Taskmanager and track the memory usage of the Java app; you will see the memory size grow with every display change.  If you want to see dramatic leaks, increase the size of the Java window to be the same as your desktop size (maximize the window).

###@###.### 2005-1-06 22:33:40 GMT

                                    

Comments
EVALUATION

The core problem here comes from the fix to an earlier bug related to multimon Swing apps: 
	4895978: High CPU utilization with Matrox graphic adapters and Multiple Display

The problem in that older bug was that Swing would recreate the back buffer every time it rendered to a new GraphicsConfiguration.  On a multimon system where Swing windows were on more than one display, this could happen as often as every frame; rendering first to one display and then to another would cause Swing to ditch and recreate the VolatileImage back buffer every time.

The fix to that bug was to cache the VolatileImage back buffer per-GC, so rendering to a different GraphicsConfig than last time would merely cause Swing to retrieve the appropriate volatileImage instead of creating a new one.

The problem with this bug (and with the old fix) was that _all_ volatileImage back buffers are now cached, whether their GraphicsConfigs are current/valid or whether they are obsolete and no longer usable.  In the case of a multimon system, there are multiple usable GraphicsConfigurations, so caching the VolatileImages is appropriate.  But in the case of a display mode switch, the old GraphicsConfiguration objects under the previous display mode get invalidated and will not be used again.  However, the fact that Swing has stashed a reference to the VolatileImage for this obsolete GraphicsConfiguration means that that VolatileImage will not get garbage collected, and thus any resources it allocated (such as an image to hold the pixel data) will continue taking up memory for the life of the process.

The best fix here may involve some new API for GraphicsConfiguration; we could add the capability for a GraphicsConfig to realize that it is invalid (it currently does not track this information, but it easily could) and add API to GraphicsConfiguration to query that flag:
	public boolean GraphicsConfiguration.isValid();

When Swing tries to get the VolatileImage for the current GraphicsConfiguration, if it gets "null" from the hashmap that stores
these images, that can be a signal that it is now in a new display mode
and that it should walk the hasmap and remove any obsolete entires.
For every volatileImage/GC pair in the map, it can query whether that
GC is valid and, if not, it should remove that entry from the map and thus make it available for garbage collecting.

###@###.### 2005-1-06 22:33:40 GMT
                                     
2005-01-06
WORK AROUND

Every time you change display do:

RepaintManager.setCurrentManager(null);

This will trigger creating a new RepaintManager which will flush the necessary caches.
###@###.### 2005-2-17 22:51:49 GMT
                                     
2005-02-17
EVALUATION

One way to address this is for Swing to trash its volatile images cache
when a display mode change occurs. Fortunately we get a display mode
when the system wakes up after hibernation or sleep, so this should solve
the problem.
                                     
2006-04-27
EVALUATION

Since the suggested fix is in Swing code, reassigning to Swing.
                                     
2006-05-08
EVALUATION

Dmitri's suggestion is the way to go. The RepaintManager will add a DisplayChangeListener, when the display changes it'll nuke the cache.
                                     
2006-06-07
EVALUATION

4895978 was closed as a dup of 4899321. Refer to 4899321 for specifics.
                                     
2006-07-17



Hardware and Software, Engineered to Work Together