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

Native memory leak in AWT drag and drop

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P4 P4
    • 8
    • 6u22
    • client-libs
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      java version "1.6.0_xx"
      Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
      Java HotSpot(TM) Client VM (build 17.1-b03, mixed mode, sharing)

      Also applies to Java 7


      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      When dropping objects onto AWT drop targets, the native part of Java keeps references to the transferred objects, represented by byte arrays.
      I checked with both Eclipse Memory Analyzer Tool http://www.eclipse.org/mat) and JProfiler (http://www.ej-technologies.com/products/jprofiler/overview.html); they show similar results: byte arrays with no path to GC root (MAT) or with only a native (JNI) inbound reference (JProfiler).

      Please note that the attached SSCCE exercises this bug by dragging and dropping files to keep it small, but I found it when developing a widget that handles raw images in addition to image files; and when using large images you can quickly fall into OutOfMemoryErrors

      I think that is important to note that albeit drag and drop and copy and paste share much of data transfer infrastructure, the same does NOT happen with copy and paste.
      The attached SSCCE does not implements copy and paste, but the original widget does, so I tested it.

      I can post another SSCCE (not so small, about 450 LoC) that handles images and implements copy and paste.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - compile the attached SSCCE
        javac DndLeakSscce.java

      - run it:
        java DndLeakSscce
        
      - drag some files and drop them onto the frame's content panel; do it several times;

      - take a snapshot of the heap and search for retained byte arrays; among all of them,
        there are as many as the number of the DnD operations of the previous step;
        discriminate the relevant ones by inspecting them (they contain the files names)

      - for each of them, search the path to the GC root: it is empty.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The native subsystem should release the transferred object once the DnD operation is completed.

      ACTUAL -
      The native subsystem keeps references to all transferred objects (as many as the number of DnD operations) even if the DnD operations are completed, preventing the GC to collect them. After some DnD operations, depending on the size of the transferred objects, the JVM exhausts its memory.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.Dimension;
      import java.awt.datatransfer.DataFlavor;
      import java.awt.datatransfer.Transferable;
      import java.io.File;
      import java.util.List;
      import javax.swing.JComponent;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;
      import javax.swing.TransferHandler;


      public class DndLeakSscce {
          private void createAndShowGUI() {
      JLabel label = new JLabel("Drop some files on me");
      label.setPreferredSize(new Dimension(300, 60));

      ImageTransferHandler transferHandler = new ImageTransferHandler(label);
      label.setTransferHandler(transferHandler);

              JFrame frame = new JFrame("AWT DnD memory leak SSCCE");
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      JPanel contentPane = new JPanel();
              contentPane.add(label);
              frame.setContentPane(contentPane);

              frame.pack();
              frame.setVisible(true);
          }

          public static void main(String[] args) {
      final DndLeakSscce tester = new DndLeakSscce();

              javax.swing.SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
               tester.createAndShowGUI();
                  }
              });
          }


          private class ImageTransferHandler
      extends TransferHandler
          {
      private static final long serialVersionUID = -7181487905861658419L;
      private JLabel label;

      public ImageTransferHandler(JLabel label) {
      super();
      this.label = label;
      }

      public boolean canImport(JComponent component, DataFlavor[] transferFlavors) {
      for (DataFlavor flavor: transferFlavors) {
      if (flavor.equals(DataFlavor.javaFileListFlavor)) {
      return true;
      }
      }

      return false;
      }

      @SuppressWarnings("unchecked")
      public boolean importData(JComponent component, Transferable transferable) {
      try {
      List<File> files = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
      label.setText("You dropped " + files.size() + " files");
      return true;
      } catch (Exception e) {
      e.printStackTrace();
      return false;
      }
      }
          }
      }


      ---------- END SOURCE ----------

            denis Denis Fokin (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: