JDK-6321947 : GC does not complete after Frame.dispose() until focus/unfocus of another Frame
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: solaris_10
  • CPU: x86
  • Submitted: 2005-09-09
  • Updated: 2011-02-16
  • Resolved: 2007-06-07
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java full version "1.5.0_03-b07"
java full version "1.5.0_04-b05"
java full version "1.6.0-ea-b44"
java full version "1.4.2_08-b03"

ADDITIONAL OS VERSION INFORMATION :
SunOS snake 5.10 Generic_118844-08 i86pc i386 i86pc

EXTRA RELEVANT SYSTEM CONFIGURATION :
Stock W1100z all contract patched applied

A DESCRIPTION OF THE PROBLEM :
It appears that a possible circular reference or code issue via PhantomReference and/or WeakReference, prevents GC from completeing after a Frame.dispose() call until a focus/unfocus event on another Frame occurs.

*) Note, tiny memory objects associated with normal Frame creation/disposal are
    never freed from the JVM runtime system might cause a 'stick'  preventing full
   GC of a normally disposed Frame.  This I beleive leads to the issue of
   applications not get fully garbage collected until a focus/unjfocus on another GUI
   item.

The test case will if profiled will show 5MB pinned in the Heap when of course it
could be free'd.

The work around is explicit <var>=null the S_bigDummyData varible at the time
of the Frame.dispose() call which is pretty unacceptible, and a real anti-Java practice.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Using any (decent) profiler we do the following (describes JVM 5.0)
1) Start Application (see source code below)
2) Hit the "Create New Frame" button once
3) Close the "popup Frame called "new sub frame"

ALL MEMORY of the sub-frame should be FREE
right now, however it doesn't get free'd until ...

4) Get the 'focus' on the Main Application Frame
     on/off a few time will ffree up some misc (another BUG to be
    reported) memory that may have been allocated with the
    Frame e.g. S_bigDummyData.
*) Perform GC/and snapshot the current state as a baseline
6) Hit the "Create New Frame" button once
7) Close the "popup Frame called "new sub frame"
*) Get the 'focus' on the Main Application Frame
    and 'unfocus' it to free up some misc (another BUG to be
    reported) memory that may have been allocated with the
    Frame e.g. S_bigDummyData.

At this point we always show the following leaks one or more
    java.lang.ref.PhantomReference
    sun.java2d.DefaultDisposerRecord
    java.util.Hashtable$Entry
    java.lang.ref.SoftReference
    java.lang.ref.WeakReference
    sun.java2d.pipe.Region

If YOU do not do a get/release focus on the main application's
frame the S_bigDummyData byte array of about 5MB will not
free up!   (This will be opened in another BUG report). Note
the get/release focus will free the 5MB for this simple example
but will not free data that has a lot of complex references between
multiple classes (which my test case can not show).

So this is really two bugs, a small leak as described above
and a LARGE temporary leak of data associated with sub-frames
that only goes away when a 'focus' of the main application
screen occurs.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The heap should free 5MB after step 3) above without requiring a focus/unfocus of
another Frame.
ACTUAL -
5MB heap is stuck and can not be GC'd without requiring a focus/unfocus of
another Frame.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Not Applicable, this is a JVM heap leak

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// CODE SUBMISSION IS FOR B) BELOW I WILL OPEN UP NEW BUGS
// FOR THE OTHER ISSUES IN A FEW DAYS FOR PROPER BUGID
// REFERENCES.
//
// Test Case XtoolkitIssue.java, shows two issues (A and B):
// A) Tiny memory objects associated with normal Frame
//    creation/disposal are never freed from the JVM
//    runtime system might cause a 'stick' preventing GC
//    of a normally disposed Frame.
// B) A LARGE temporary leak of data associated with
//    sub-frames that only goes away when a new 'focus'
//    to the main application screen occurs. The dispose()
//    method call of a given Frame should not require a
//    focus/unfocus of another GUI Frame to free up all
//    associated memory and or references.
// C) An even more important issue is that large complex
//    applications (multi-window) that extend Frame/JFrame
//    may not actually get GC'd and thus cause memory
//    exhustion releated to A) above and possible circular
//    references via PhantomReference and/or WeakReference,
//    but this is beyond the scope of a simple test case
//    (only happens under Solaris 10 x86 with the XAWT
//    setting -Dawt.toolkit=sun.awt.X11.XToolkit).  It
//    never happens under 1.4.2 or eariler.
//
// May be related to 6257260, 6245243, 6240100 - the synopsys
// seems to support C) "The test is still fails if WeakRefs
// are used. Seems that GC still doesn't clear heap area.", I
// think 'the test' that is refered to doesn't fully excercise
// the bug as a major application will work under JVM's 5.0
// and 6.0 if and only if XToolkit is NOT used, if XToolkit
// is used we seem to get a GC leak that will quickly exhust
// memory, the work around is explicit <var>=null to hundreds of
// variables in eleven different classes at the time of a
// Frame.dispose() call which is pretty unacceptible.
//
// -----------------------------------------------------
// The LEAK occurs under
//
// under Solaris 10 x86 with either
//     -Dawt.toolkit=sun.awt.X11.XToolkit
// or
//     setenv AWT_TOOLKIT XToolkit
//
// uname -a (Stock W1100z with all contract patches)
//     SunOS snake 5.10 Generic_118844-08 i86pc i386 i86pc
//
// Under JVM's
//     java full version "1.5.0_03-b07"  -> issues A) B) and C)
//     java full version "1.5.0_04-b05"  -> issues A) B) and C)
//     java full version "1.6.0-ea-b44"  -> issues A) B) and C)
//     java full version "1.4.2_08-b03"  -> issues B) only
//  -----------------------------------------------------
// Using any (decent) profiler we do the following
// 1) Start Application (see source code below)
// 2) Hit the "Create New Frame" button once
// 3) Close the "popup Frame called "new sub frame"
//
// ALL MEMORY of the sub-frame should be FREE
// right now, however it doesn't get free'd until ...
//
// 4) Get the 'focus' on the Main Application Frame
//     on/off a few time will ffree up some misc (another BUG to be
//     reported) memory that may have been allocated with the
//     Frame e.g. S_bigDummyData.
// *) Perform GC/and snapshot the current state as a baseline
// 6) Hit the "Create New Frame" button once
// 7) Close the "popup Frame called "new sub frame"
// *) Get the 'focus' on the Main Application Frame
//     and 'unfocus' it to free up some misc (another BUG to be
//     reported) memory that may have been allocated with the
//     Frame e.g. S_bigDummyData.
//
// At this point we always show the following leaks one or more
//     java.lang.ref.PhantomReference
//     sun.java2d.DefaultDisposerRecord
//     java.util.Hashtable$Entry
//     java.lang.ref.SoftReference
//     java.lang.ref.WeakReference
//     sun.java2d.pipe.Region
//
// Now don't say this is fine, as it appears that it doesn't
// really hurt as complex applications can unlike the freeing
// of the S_bigDummyData on 'focus/unfocus' never free
// data from the heap.
//
// If YOU do not do a get/release focus on the main application's
// frame the S_bigDummyData byte array of about 5MB will not
// free up!   (This will be opened in another BUG report). Note
// the get/release focus will free the 5MB for this simple example
// but will not free data that has a lot of complex references between
// multiple classes (which my test case can not show).
//
// So this is really two bugs, a small leak as described above
// and a LARGE temporary leak of data associated with sub-frames
// that only goes away when a 'focus' of the main application
// screen occurs.
// -----------------------------------------------------
// Note, if we remove
//     -Dawt.toolkit=sun.awt.X11.XToolkit
// or
//     unsetenv AWT_TOOLKIT
// then there is a still a tiny one (1) object "leak" in the form of
//     sun.awt.motif.MwindowAttributes
// and the LARGE temporary leak (still exists) of data associated
// with sub-frames that only goes away when a 'focus/unfocus' of
// the main application screen occurs.
// -----------------------------------------------------
// Note, if we use JDK 1.4 e.g.
//     java full version "1.4.2_08-b03"
// there are still some tiny object "leak" in the form of
//     sun.awt.motif.MwindowAttributes (and others)
// and the LARGE temporary leak (still exists) of data associated
// with sub-frames that only goes away when a 'focus/unfocus' of
// the main application screen occurs.
 


import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

public class XtoolkitIssue extends Frame implements WindowListener{

    private boolean S_etype = true;
    private Button S_b = null;
    private static int S_offset = 100;
    public byte[] S_bigDummyData = null;

    public XtoolkitIssue(String name, boolean etype /* if true then exit app on windowClose() */){
        S_etype = etype;
        setBounds(0,0,250,100);
        setVisible(true);
	setTitle(name);
        addWindowListener(this);
    }
    
    public void addLaunchButton()
    {
        setLayout(new GridLayout(1,1));
        S_b = new Button("create new frame");
        add(S_b);
        validate();
    }
    
    public boolean action(Event event, Object arg)
    {
        if (event.target.equals(S_b)) {
            // odd that the number of Frame never decrease ?
            Frame ff[] = java.awt.Frame.getFrames();
            System.out.println("java.awt.Frame.getFrames().length=="+ff.length);
            ff = null;
            System.out.println("action for b: " + event.toString());
            XtoolkitIssue popup = new XtoolkitIssue("new sub frame",false);
            S_offset = S_offset + 50;
            System.out.println(S_offset);
            popup.setBounds(S_offset, S_offset, 250,100);
            popup.S_bigDummyData = new byte[1000*1000*5];
        }
        return true;
    }
    
    public void windowOpened(WindowEvent we){
	/* actually windowFirst-Opened would be a better name */
    }

    public void windowClosed(WindowEvent we){
	/* actually WindowDisposed would be a better name */
	System.out.println("windowClosed (AKA WindowDisposed) " +we.getWindow());

	// We could invoke the following, but they will not solve the leak
        // in sun.awt.X11.XToolkit
        //   we.getWindow().removeWindowListener(this);
	//   removeNotify();
        //   removeNotify();
        //   System.runFinalization();
    }

    public void windowActivated(WindowEvent we){
    }

    public void windowDeactivated(WindowEvent we){
    }

    public void windowDeiconified(WindowEvent we){
    }

    public void windowIconified(WindowEvent we){
    }

    public void windowClosing(WindowEvent we){
	System.out.println("windowClosing " +we.getWindow());

        dispose();
	if (S_etype == true) {
            // only the primary window invokes an application exits
	    System.out.println("Application Exiting ...");
	    System.exit(0);
	}
    }

    public static void main(String args[]){
        XtoolkitIssue Xtest = new XtoolkitIssue("main frame",true);
        Xtest.addLaunchButton();
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
A) focus/unfocus of another Frame, however this is not always possible, saying that a user
     will manipulate the GUI and eventually trigger that focus/unfocus to allow GC to
     complete is pretty lame, in fact it can result in a very unresponsive application as GC
     was deffered until a end-user manipulates a GUI.

B)  The work around is explicit <var>=null the S_bigDummyData varible at the time
      of the Frame.dispose() call which is pretty unacceptible, and a real anti-Java practice.
      For large applications this could be thosands of <var>=null statements.

Comments
EVALUATION it looks like last focused frame was not gc-ed because it was stored in DKFM.realOppositeWindow field. This problem has been fixed (see 6469530) Thus the problem is not reproduceible any more.
07-06-2007

EVALUATION Yes, the fact that focus engine keeps references to the focus state fields is known, it is described in 4726458.
09-09-2005