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

Memory leak in com.apple.laf.ScreenMenu: removed JMenuItems are still referenced

    Details

    • Subcomponent:
    • Resolved In Build:
      b127
    • CPU:
      x86
    • OS:
      other

      Backports

        Description

        FULL PRODUCT VERSION :
        java version "1.8.0_91"
        Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
        Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

        ADDITIONAL OS VERSION INFORMATION :
        Darwin saotome.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64

        A DESCRIPTION OF THE PROBLEM :
        When using the system menu bar on OS X (System.setProperty("apple.laf.useScreenMenuBar", "true")), any JMenuItem you remove from a JMenu is still referenced as long as the menu bar exists.

        The problem is located in the ScreenMenu class:

        1) The ScreenMenu class attaches a ContainerListener to the invoker (=the JMenu instance) in the addNotify method . However, the JMenu never fires ContainerEvents. It is the JMenu#getPopupMenu that fires the events. As such, the cleanup code in com.apple.laf.ScreenMenu#componentRemoved is never triggered

        2) In the com.apple.laf.ScreenMenu#componentRemoved, the entry from fItems is never removed, because the remove method is called with the value instead of the key

        3) The com.apple.laf.ScreenMenu#updateItems method removes all entries through a removeAll call, but does not remove those entries from the fItems map (nor does it always clean the childHashArray, but that is no memory leak)

        The attached test program illustrates that the JMenuItem, which has been removed from the JMenuBar, is still referenced.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Run the attached program

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Expected result is a successful termination of the program
        ACTUAL -
        A RuntimeException is thrown, because the JMenuItem is still referenced.

        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        import java.awt.EventQueue;
        import java.lang.ref.WeakReference;
        import java.lang.reflect.InvocationTargetException;
        import java.util.Objects;

        import javax.swing.JFrame;
        import javax.swing.JLabel;
        import javax.swing.JMenu;
        import javax.swing.JMenuBar;
        import javax.swing.JMenuItem;
        import javax.swing.WindowConstants;

        public class MenuBarMemoryLeakTest {
          private static byte[] sBytes;

          private static WeakReference<JMenuItem> sMenuItem;
          private static JFrame sFrame;
          private static JMenu sMenu;

          public static void main(String[] args) throws InvocationTargetException, InterruptedException {
            EventQueue.invokeAndWait(new Runnable() {
              @Override
              public void run() {
                System.setProperty("apple.laf.useScreenMenuBar", "true");
                showUI();
              }
            });

            EventQueue.invokeAndWait(new Runnable() {
              @Override
              public void run() {
                removeMenuItemFromMenu();
              }
            });
            fillUpMemory();
            JMenuItem menuItem = sMenuItem.get();
            EventQueue.invokeAndWait(new Runnable() {
              @Override
              public void run() {
                sFrame.dispose();
              }
            });
            if ( menuItem != null ){
              throw new RuntimeException("The menu item should have been GC-ed");
            }
          }

          private static void showUI(){
            sFrame = new JFrame();
            sFrame.add(new JLabel("Some dummy content"));

            JMenuBar menuBar = new JMenuBar();

            sMenu = new JMenu("Menu");
            JMenuItem item = new JMenuItem("Item");
            sMenu.add(item);

            sMenuItem = new WeakReference<>(item);

            menuBar.add(sMenu);

            sFrame.setJMenuBar(menuBar);
            sFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
            sFrame.pack();
            sFrame.setVisible(true);
          }

          private static void removeMenuItemFromMenu(){
            JMenuItem menuItem = sMenuItem.get();
            Objects.requireNonNull(menuItem,"The menu item should still be available at this point");
            sMenu.remove(menuItem);
          }

          /**
           * Fill up the available heap space to ensure that any Soft and WeakReferences gets cleaned up
           */
          private static void fillUpMemory(){
            int size = 1000000;
            for (int i = 0; i < 50; i++) {
              System.gc();
              System.runFinalization();
              try {
                sBytes = null;
                sBytes = new byte[size];
                size = (int) (((double) size) * 1.3);
              } catch (OutOfMemoryError error) {
                size = size / 2;
              }
              try {
                if (i % 3 == 0) {
                  Thread.sleep(321);
                }
              } catch (InterruptedException t) {
                // ignore
              }
            }
            sBytes = null;
          }
        }

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

        CUSTOMER SUBMITTED WORKAROUND :
        Workaround is not using the system menu bar.

          Activity

          Show
          alexsch Alexander Scherbatiy (Inactive) added a comment - The proposed fix: http://cr.openjdk.java.net/~alexsch/robin.stevens/8158325/webrev.00 See discussion: http://mail.openjdk.java.net/pipermail/swing-dev/2016-June/006200.html
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/client/jdk/rev/3bcead661fa6
          User: alexsch
          Date: 2016-06-30 16:07:49 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/client/jdk/rev/3bcead661fa6 User: alexsch Date: 2016-06-30 16:07:49 +0000
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/3bcead661fa6
          User: amurillo
          Date: 2016-07-13 03:50:56 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/3bcead661fa6 User: amurillo Date: 2016-07-13 03:50:56 +0000

            People

            • Assignee:
              azvegint Alexander Zvegintsev (Inactive)
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: