Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6616095

AWT's WindowDisposerRecord keeps AppContext alive too long

XMLWordPrintable

    • b25
    • generic
    • generic
    • Not verified

        The Java 2D Disposer is keeping Java objects alive too long. This
        mechanism was introduced into Java 2D to avoid the use of finalizers,
        which require two full garbage collections to reclaim all of the
        associated storage. Due to the structure of the Disposer, it still
        requires two full GCs to reclaim storage associated with dead
        AppContexts.

        The following two graph traces come from the new Java Plug-In (not yet
        integrated into 6u5) and are meant to illustrate what is keeping
        objects alive unnecessarily. An applet (MemoryLeakTest) was loaded
        which allocated a significant amount of memory, and then we switched
        away from the web page to another applet. The heap dump was taken at
        this point. At this stage, the AppContext for the MemoryLeakTest
        applet has been disposed and all plugin-related resources cleaned up.
        There should not be any strong references to the applet left at this
        point. However, it is clear that the AppContext, and ultimately the
        applet, are still reachable through the value field of the associated
        Hashtable entry in the Disposer's global table mapping references to
        DisposerRecords. Attempting to reload the MemoryLeakTest provokes an
        OutOfMemoryError because the JVM only performs one full GC to try to
        satisfy the allocation done by the second MemoryLeakTest instance.

        (The reason that this issue does not happen with the old Java Plug-In
        is that it eagerly nulls out the reference to the applet, which is a
        hack that should not be necessary. We are going to introduce a similar
        hack into the new Java Plug-In to work around this problem under
        6613370.)

        Static reference from sun.java2d.Disposer.records (from class sun.java2d.Disposer) :
        --> java.util.Hashtable@0x2e6f8e8 (40 bytes) (field table:)
        --> [Ljava.util.Hashtable$Entry;@0x2fdfbd8 (196 bytes) (Element 39 of [Ljava.util.Hashtable$Entry;@0x2fdfbd8:)
        --> java.util.Hashtable$Entry@0x2fec8c8 (24 bytes) (field next:)
        --> java.util.Hashtable$Entry@0x2fee6a0 (24 bytes) (field value:)
        --> java.awt.Window$WindowDisposerRecord@0x2fef850 (20 bytes) (field context:)
        --> sun.awt.AppContext@0x2fdf9b8 (49 bytes) (field contextClassLoader:)
        --> sun.plugin2.applet.Applet2ClassLoader@0x2fdc358 (98 bytes) (field manager:)
        --> sun.plugin2.applet.Applet2Manager@0x2fdc2e8 (105 bytes) (field applet:)
        --> MemoryLeakTest@0x2febfb0 (260 bytes) (field largeArray:)
        --> [B@0x2ff5fd8 (41943048 bytes)

        Static reference from sun.java2d.Disposer.records (from class sun.java2d.Disposer) :
        --> java.util.Hashtable@0x2e6f8e8 (40 bytes) (field table:)
        --> [Ljava.util.Hashtable$Entry;@0x2fde8b8 (196 bytes) (Element 39 of [Ljava.util.Hashtable$Entry;@0x2fde8b8:)
        --> java.util.Hashtable$Entry@0x2febba0 (24 bytes) (field next:)
        --> java.util.Hashtable$Entry@0x2feda30 (24 bytes) (field value:)
        --> java.awt.Window$WindowDisposerRecord@0x2fef230 (20 bytes) (field context:)
        --> sun.awt.AppContext@0x2febc20 (49 bytes) (field contextClassLoader:)
        --> sun.plugin2.applet.Applet2ClassLoader@0x2fdb5c8 (98 bytes) (field manager:)
        --> sun.plugin2.applet.Applet2Manager@0x2fdb680 (105 bytes) (field listeners:)
        --> java.util.ArrayList@0x2fdf048 (20 bytes) (field elementData:)
        --> [Ljava.lang.Object;@0x2fe6418 (48 bytes) (Element 0 of [Ljava.lang.Object;@0x2fe6418:)
        --> sun.plugin2.main.client.PluginMain$1$2@0x2febb60 (16 bytes) (field val$helper:)
        --> sun.plugin2.main.client.DragHelper@0x2fed970 (45 bytes) (field component:)
        --> MemoryLeakTest@0x2ff4050 (260 bytes) (field largeArray:)
        --> [B@0x2ff5fe8 (41943048 bytes)

        Conceptually the DisposerRecord should record the minimal amount of
        information needed in order to clean up the associated resource. It
        should not need a reference to the AppContext. At this point in the
        code, the AppContext has been disposed and is effectively dead.

        The steps in reclaiming the disposer records are that the weak or
        phantom reference pointing to the object in question is cleared during
        a full GC; the reference is enqueued; the Disposer polls the queue,
        doing the cleanup work and removing the record from the "records"
        table; and then another full GC occurs, removing the disposer record
        and allowing the value and the object graph referenced from it to be
        GCd.

        Again, the disposer record should point to the minimal amount of
        information needed to clean up any native resources associated with
        the given object. It should clearly not point to the AppContext
        because doing so is meaningless at least in situations like the above
        where the AppContext has already been disposed.

        This issue may also be causing native resources to be held on to
        longer than they should be, depending on the object graphs referenced
        by the disposer records.
        After discussion with ###@###.### and ###@###.###, it seems clear that the problem lies specifically in the java.awt.Window$WindowDisposerRecord class and not in the Java 2D Disposer generally, which does not know about AppContexts. Jim states:

        > ...their private implementation of a disposer already knows enough to hold onto
        > weak references to the window itself - they just need to extend that philosophy
        > one further to holding on to a weak reference to the AppContext as well.
        > Currently they hold onto a non-weak reference to it. If the context was
        > reclaimed and the weak reference is empty then they don't need to remove
        > the window from the list.

        and Dmitri points out this forum thread for more information on writing Disposer records:

          http://forums.java.net/jive/thread.jspa?threadID=31821&tstart=0

        Reassigning to AWT.

              son Oleg Sukhodolsky (Inactive)
              kbr Kenneth Russell (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: