JDK-6321941 : GC failures via PhantomReference / WeakReference after Frame.dispose()
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_10
  • CPU: x86
  • Submitted: 2005-09-09
  • Updated: 2011-02-16
  • Resolved: 2006-08-04
Related Reports
Duplicate :  
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"


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 arge complex applications (multi-window) that extend Frame/JFrame
may not actually get correctly GC'd and thus cause memory exhustion releated to *)
below e.g. a possible circular reference or code issue via PhantomReference and/or WeakReference, but this is beyond the scope of a simple test case which only highlights
the *) condition.

This issue only happens under Solaris 10 x86 with the XAWT e.g.
setting -Dawt.toolkit=sun.awt.X11.XToolkit).  It never happens under 1.4.2 or
eariler JVMs.

*) 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 leeds to a timiimg condition
   or race in which complex applications do not get fully garbage collected.

May be related to 6257260, 6245243, 6240100 - the synopsys seems to support
my claim in this bug report

     "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
written by my company 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
all memory in seconds, the work around is explicit <var>=null to hundreds of variables
in eleven different classes [at least for this App] at the time of a 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
1) Start Application (see source code below)
2) Hit the "Create New Frame" button once
3) Close the "popup Frame called "new sub frame"
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.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
All HEAP associated with the Frame should free up
ACTUAL -
HEAP associated with the Frame frees up e.g. S_bigDummyData only on a focus/unfocus
on another GUI Frame.  However this behaviour will not work on complex HEAP structures, which are beyond the scope of a simple test case.

Please note, if XToolkit is NOT used for 5.0 and 6.0 JVMs complex Heap allocation is indeed
free'd up and also if a JVM of 1.4.2 or below is used complex Heap allocation is indeed
free'd up on all Frame.dispose() calls.   This is whay I assert that this is a timing issue in the
GC sub-system due to

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 C) 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"
// 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) make sure that XToolkit is NOT used, however this
     results in horrible Fonts for Java AWT based applications
     due to Solaris 10 x86 screen DPI issues.

B) Help the GC subsystem by doing explicit <var>=null
     to hundreds or thousands of variables i different
     classes at the time of a Frame.dispose() call.  This wokrs
     but can take a day or two for every major Application and
     this seems pretty ANTI-java and thus is  pretty unacceptible.

Comments
EVALUATION As far as I understand this CR is related to C) issue: focus/unfocus will not free up HEAP if there are complex structures in the test case. If we fix the problem about focus (6321947) the behaviour of the complex application should be revised.
30-09-2005

EVALUATION I am not sure I understand what this bug is about giving other similar bugs: 6321949, 6321947, especially since description is the same (or I don't see the difference?).
09-09-2005