Hello all,

my OCA has just been accepted.
Let me know if there are any other steps I need to take to get this review
started.

Kind regards,

Robin

On Wed, Jun 8, 2016 at 9:17 AM, Robin Stevens <robin.stev...@scz.be> wrote:

> Hello all,
>
> are there any additional steps I need to take to get a review of this
> patch started ?
>
> Kind regards,
>
> Robin
>
> On Tue, May 31, 2016 at 5:01 PM, Robin Stevens <robin.stev...@scz.be>
> wrote:
>
>> Hello,
>>
>> I created a patch for a bug I just logged through http://bugs.java.com/
>> (still under review with identifier JI-9038899).
>>
>> The com.apple.laf.ScreenMenu class keeps hard references to JMenuItems
>> which have been removed from the JMenu.
>>
>> The patch contains a fix for the memory leak and a test case which
>> reveals the issue.
>> The attached patch is a diff against
>> http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/rev/4d6c03fb1039 .
>>
>> There were a few issues with the ScreenMenu class:
>>
>> - The ContainerListener was attached to the JMenu and not to the
>> JMenu#getPopupMenu. The JMenu itself does not fire any ContainerEvents, but
>> the popup does. As a result, the cleanup code in ScreenMenu was never
>> triggered.
>> The patch fixes this by attaching the ContainerListener to the popup menu.
>>
>> Note that the ScreenMenu class also attaches a ComponentListener to the
>> JMenu. I had no idea whether that one must be attached to the popup menu as
>> well, so I did not change it.
>>
>> - The cleanup code was not triggered when removeAll() was called from the
>> updateItems method. I fixed this by overriding the remove(int) method, and
>> putting the cleanup code in that method.
>>
>> An alternative here would be to not override the remove(int) method, but
>> instead call fItems.clear() after calling removeAll() . However, overriding
>> the remove(int) method sounded more robust to me.
>>
>> - The cleanup code was incorrect. It tried to remove an item from fItems
>> (a map) by calling remove with the value instead of the key. Now the remove
>> is called with the key. Because the cleanup code has been moved, this
>> required me to loop over the map as I have no direct access to the key in
>> the remove(int) method
>>
>> - The test can be run on all platforms, although it was written for an OS
>> X specific bug. As it can run on all platforms, I did not disable it on non
>> OS X platforms. Let me know if I need to adjust this.
>>
>>
>> Kind regards,
>>
>> Robin
>>
>> PS: I only just started contributing. Let me know if I did something
>> incorrect in my workflow.
>>
>> Patch (output of hg diff -g):
>>
>> diff --git a/src/macosx/classes/com/apple/laf/ScreenMenu.java
>> b/src/macosx/classes/com/apple/laf/ScreenMenu.java
>> --- a/src/macosx/classes/com/apple/laf/ScreenMenu.java
>> +++ b/src/macosx/classes/com/apple/laf/ScreenMenu.java
>> @@ -29,6 +29,8 @@
>>  import java.awt.event.*;
>>  import java.awt.peer.MenuComponentPeer;
>>  import java.util.Hashtable;
>> +import java.util.Map;
>> +import java.util.Set;
>>
>>  import javax.swing.*;
>>
>> @@ -109,6 +111,7 @@
>>          final Component[] items = fInvoker.getMenuComponents();
>>          if (needsUpdate(items, childHashArray)) {
>>              removeAll();
>> +            childHashArray = null;
>>              if (count <= 0) return;
>>
>>              childHashArray = new int[count];
>> @@ -232,7 +235,7 @@
>>          synchronized (getTreeLock()) {
>>              super.addNotify();
>>              if (fModelPtr == 0) {
>> -                fInvoker.addContainerListener(this);
>> +                fInvoker.getPopupMenu().addContainerListener(this);
>>                  fInvoker.addComponentListener(this);
>>                  fPropertyListener = new ScreenMenuPropertyListener(this);
>>                  fInvoker.addPropertyChangeListener(fPropertyListener);
>> @@ -266,13 +269,35 @@
>>              if (fModelPtr != 0) {
>>                  removeMenuListeners(fModelPtr);
>>                  fModelPtr = 0;
>> -                fInvoker.removeContainerListener(this);
>> +                fInvoker.getPopupMenu().removeContainerListener(this);
>>                  fInvoker.removeComponentListener(this);
>>                  fInvoker.removePropertyChangeListener(fPropertyListener);
>>              }
>>          }
>>      }
>> -
>> +
>> +    @Override
>> +    public void remove(int index) {
>> +        MenuItem item;
>> +        synchronized (getTreeLock()) {
>> +            item = getItem(index);
>> +        }
>> +        super.remove(index);
>> +        if(item != null){
>> +            Set<Map.Entry<Component, MenuItem>> entrySet =
>> fItems.entrySet();
>> +            Component keyToRemove = null;
>> +            for(Map.Entry<Component, MenuItem> entry : entrySet){
>> +                if(entry.getValue() == item){
>> +                    keyToRemove=entry.getKey();
>> +                    break;
>> +                }
>> +            }
>> +            if(keyToRemove != null){
>> +                fItems.remove(keyToRemove);
>> +            }
>> +        }
>> +    }
>> +
>>      /**
>>       * Invoked when a component has been added to the container.
>>       */
>> @@ -289,9 +314,7 @@
>>          final Component child = e.getChild();
>>          final MenuItem sm = fItems.get(child);
>>          if (sm == null) return;
>> -
>>          remove(sm);
>> -        fItems.remove(sm);
>>      }
>>
>>      /**
>> diff --git a/test/com/apple/laf/ScreenMenu/ScreenMenuMemoryLeakTest.java
>> b/test/com/apple/laf/ScreenMenu/ScreenMenuMemoryLeakTest.java
>> new file mode 100644
>> --- /dev/null
>> +++ b/test/com/apple/laf/ScreenMenu/ScreenMenuMemoryLeakTest.java
>> @@ -0,0 +1,106 @@
>> +/*
>> + * @test
>> + * @summary [macosx] Memory leak in com.apple.laf.ScreenMenu
>> + * @run main/timeout=300/othervm -Xmx12m ScreenMenuMemoryLeakTest
>> + */
>> +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 ScreenMenuMemoryLeakTest {
>> +
>> +    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;
>> +    }
>> +}
>>
>>
>>
>>
>>
>

Reply via email to