JDK-6209673 : Memory leak in Swing applications after a display mode change
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.2,5.0
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2003,windows_xp
  • CPU: x86
  • Submitted: 2004-12-16
  • Updated: 2010-04-02
  • Resolved: 2006-08-02
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.
JDK 6
6 b94Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
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 4895978 was closed as a dup of 4899321. Refer to 4899321 for specifics.
17-07-2006

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

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

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.
27-04-2006

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
17-02-2005

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
06-01-2005