JDK-4343272 : Yet another java.awt.dnd.DropTarget causing JNI Global Reference leak?
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.2.2_007
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_7
  • CPU: sparc
  • Submitted: 2000-06-05
  • Updated: 2022-10-21
  • Resolved: 2000-10-30
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.
Other Other Other Other
1.2.2_005 005Fixed 1.3.0_02Fixed 1.3.1Fixed 1.4.0Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
This bug is similar to 4224888 and 4294016, but these bugs can not be reproduced on JDK1.2.2_05a, but the following example cause problems. See instructions in the code.


import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Memory leak when using DropTarget. Tested on JDK 1.2.2 on
 * Windows NT (JDK1.2.2-003) and Solaris (JDK-1.2.2-05a).
 * 
 * This simple application simulates the behaviour of our real
 * application. The user can work with a number of instances of
 * a certain window type, MainWindow. Main windows are opened
 * and closed in the the normal work flow, they are also drop
 * targets.
 * 
 * Beacause of the bug, main windows are not garbage collected
 * when they are closed. This leads to the application running out
 * of memory after a while.
 * 
 * Start the test application and open some (5) main windows from
 * the control window's File->Open. Make each main window a drop
 * target by selecting File->DropTarget in its menu. Run the garbage
 * collector from the control window's menu twice or more to get 
 * the correct heap size. Close all main windows (from the window frame),
 * then open some more (5) main windows again and make each main window
 * a drop target. Run the garbage collector again 
 * from the control window's menu (you probably have to
 * do this twice or more to get the correct heap size as the GC call is
 * asynchronous?). The heap size has now increased with a couple of
 * megabytes (each main window has a reference to a chunk of memory
 * to simulate the resources normaly held by the window's menu items,
 * action listeners, components, etc) and there are no output from
 * the MainWindow's finalizers.
 *
 * If the same procedure is repeated but the windows are not
 * made into drop targets, they are garbage collected (heap
 * doesn't increase, the finalizers are run).
 * 
 * If you use OptimizeIt to look at allocated instances, you see
 * that MainWindows that were drop targets but have been closed are
 * still referenced by a JNI global reference.
 *
 * 2000-05-30 Jonas Ekström, ###@###.###
 */
public class MiniGnip3 extends JFrame implements ActionListener {
  private JLabel memLabel_;

  public MiniGnip3() {
    super( "Control Window" );

    JMenuBar mb = new JMenuBar();
    setJMenuBar( mb );
    JMenu menu = new JMenu("File");
    mb.add( menu );
    JMenuItem item = new JMenuItem( "Open" );
    item.addActionListener( this );
    menu.add( item );
    item = new JMenuItem( "Run GC" );
    item.addActionListener( this );
    menu.add( item );
    item = new JMenuItem( "Exit" );
    item.addActionListener( this );
    menu.add( item );

    memLabel_ = new JLabel( "Heap: " + (Runtime.getRuntime().totalMemory() -
                            Runtime.getRuntime().freeMemory()) );
    memLabel_.setHorizontalAlignment( SwingConstants.RIGHT );
    getContentPane().add( memLabel_ );
  }

  private void open() {
    MainWindow win = new MainWindow();
    win.pack();
    win.show();
  }

  private void runGc() {
    System.gc();
    memLabel_.setText( "Heap: " + (Runtime.getRuntime().totalMemory() -
                                   Runtime.getRuntime().freeMemory()) );
  }

  private void exit() {
    System.exit(0);
  }

  public void actionPerformed( ActionEvent e ) {
    if( e.getActionCommand().equals( "Open" ) )
      open();
    else if( e.getActionCommand().equals( "Run GC" ) )
      runGc();
    else if( e.getActionCommand().equals( "Exit" ) )
      exit();
  }

  public Dimension getPreferredSize() {
    return new Dimension( 180, 100 );
  }

  public static void main( String[] argv ) {
    MiniGnip3 win = new MiniGnip3();
    win.pack();
    win.show();
  }
}

class MainWindow extends JFrame {
  private DndManager dm_;
  private Meg meg_;
  public MainWindow() {
    setTitle( "Main " + getName() );

    JMenuBar mb = new JMenuBar();
    setJMenuBar( mb );   
    JMenu menu = new JMenu(); 
    menu.setText( "File" );
    mb.add( menu );
                   
    JMenuItem mi = new JMenuItem( "DropTarget" );
    mi.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        dm_ = new DndManager( MainWindow.this );
        setTitle( "DT " + getTitle() );
      }
    } );
    menu.add( mi );

    addWindowListener( new CloseCommand() );
    meg_ = new Meg();
  }

  public Dimension getPreferredSize() {
    return new Dimension( 200, 60 );
  }

  private void close() {
    System.out.println( "Disposing " + getName() );
    if( dm_ != null ) {
      dm_.clear(); // to try to stop being a drop target
      dm_ = null;  // to get the DndManager GC'd
    }
    dispose();
  }

  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println( getName() + " finalized." );
  }

  private class DndManager implements DropTargetListener {
    private DropTarget dt_;
    DndManager( Component comp ) {
      dt_ = new DropTarget( comp, DnDConstants.ACTION_COPY_OR_MOVE, this );
    }

    /**
     * Perform any action required to clear the window from being
     * a drop target and the DndManager from being a drop target
     * listener. Don't know if these have any effect, is there
     * anything else that can be done?
     */
    void clear() {
      dt_.removeDropTargetListener( this );
      dt_.setComponent( null );
    }

    protected void finalize() throws Throwable {
      super.finalize();
      System.out.println( "DndManager finalized." );
    }

    public synchronized void drop( DropTargetDropEvent dtde ) {}
    public void dragEnter( DropTargetDragEvent dtde ) {}
    public void dragExit( DropTargetEvent dropTargetEvent ) {}
    public void dragOver( DropTargetDragEvent dtde ) {}
    public void dropActionChanged( DropTargetDragEvent dtde ) {}
  }

  private class CloseCommand extends WindowAdapter {
    public void windowClosing( WindowEvent e ) {
      close();
    }
  }

  private class Meg {
    int[][] memory_ = new int[512][512];
  }
}

jonas.edberg@sweden 2000-06-05

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: ladybird merlin-beta FIXED IN: 1.2.2_005 ladybird merlin-beta INTEGRATED IN: 1.2.2_005 1.3.0_02 1.3.1 ladybird merlin-beta VERIFIED IN: 1.3.0_02 1.3.1_01a
14-06-2004

WORK AROUND None
11-06-2004

SUGGESTED FIX ------- awt_XmDnD.c ------- *** /tmp/d0Uld7i Fri Aug 4 11:07:37 2000 --- awt_XmDnD.c Fri Aug 4 10:37:21 2000 *************** *** 624,630 **** return; } ! if (dropsite->dsCnt-- == 0) { XmDropSiteUnregister(cdata->widget); (*env)->DeleteGlobalRef(env, dropsite->component); --- 624,631 ---- return; } ! dropsite->dsCnt--; ! if (dropsite->dsCnt == 0) { XmDropSiteUnregister(cdata->widget); (*env)->DeleteGlobalRef(env, dropsite->component); ------- awt_Cursor.c ------- *** /tmp/sccs.8uayZo Fri Aug 4 18:12:49 2000 --- awt_Cursor.c Fri Aug 4 18:06:26 2000 *************** *** 144,153 **** return; target = (*env)->GetObjectField(env, peer, mComponentPeerIDs.target); ! if (replace != UPDATE_ONLY) { ! curComp = target; ! if (replace == CACHE_ONLY) return; } /* Initialize our java identifiers once. Checking before locking * is a huge performance win. --- 144,159 ---- return; target = (*env)->GetObjectField(env, peer, mComponentPeerIDs.target); ! if (replace != UPDATE_ONLY) { ! if (!JNU_IsNull(env, curComp)) { ! (*env)->DeleteWeakGlobalRef(env, curComp); } + curComp = (*env)->NewWeakGlobalRef(env, target); + if (replace == CACHE_ONLY) { + (*env)->PopLocalFrame(env, 0); + return; + } + } /* Initialize our java identifiers once. Checking before locking * is a huge performance win. *************** *** 174,183 **** (*env)->CallStaticVoidMethod(env, globalCursorManagerClass, updateCursorID, target); DASSERT(!((*env)->ExceptionOccurred(env))); } jobject getCurComponent() { ! return curComp; } --- 180,191 ---- (*env)->CallStaticVoidMethod(env, globalCursorManagerClass, updateCursorID, target); DASSERT(!((*env)->ExceptionOccurred(env))); + (*env)->PopLocalFrame(env, 0); } jobject getCurComponent() { ! JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); ! return (*env)->NewLocalRef(env, curComp); }
11-06-2004

EVALUATION There are both JNI global and JNI local reference leaks, in awt_XmDnD.c and awt_Cursor.c. mingyao.yang@Eng 2000-08-04
04-08-2000