7070542 - using weak references in KeyboardFocusManager leaks
authorJaroslav Tulach <jtulach@netbeans.org>
Thu, 29 Sep 2011 15:11:10 -0700
branchleaks
changeset 5223f31a4486a0ef
parent 5222 78cea258caab
child 5224 8637467d77c4
7070542 - using weak references in KeyboardFocusManager
src/share/classes/java/awt/KeyboardFocusManager.java
test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaActiveWindow.java
test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaFocusOwner.java
test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaFocusedWindow.java
     1.1 --- a/src/share/classes/java/awt/KeyboardFocusManager.java	Thu Mar 29 13:02:24 2012 -0700
     1.2 +++ b/src/share/classes/java/awt/KeyboardFocusManager.java	Thu Sep 29 15:11:10 2011 -0700
     1.3 @@ -38,6 +38,7 @@
     1.4  import java.beans.VetoableChangeListener;
     1.5  import java.beans.VetoableChangeSupport;
     1.6  
     1.7 +import java.lang.ref.Reference;
     1.8  import java.lang.ref.WeakReference;
     1.9  
    1.10  import java.lang.reflect.Field;
    1.11 @@ -56,7 +57,6 @@
    1.12  import sun.util.logging.PlatformLogger;
    1.13  
    1.14  import sun.awt.AppContext;
    1.15 -import sun.awt.HeadlessToolkit;
    1.16  import sun.awt.SunToolkit;
    1.17  import sun.awt.CausedFocusEvent;
    1.18  import sun.awt.KeyboardFocusManagerPeerProvider;
    1.19 @@ -287,19 +287,19 @@
    1.20       * The Component in an application that will typically receive all
    1.21       * KeyEvents generated by the user.
    1.22       */
    1.23 -    private static Component focusOwner;
    1.24 +    private static Reference<Component> focusOwner = new WeakReference<>(null);
    1.25  
    1.26      /**
    1.27       * The Component in an application that will regain focus when an
    1.28       * outstanding temporary focus transfer has completed, or the focus owner,
    1.29       * if no outstanding temporary transfer exists.
    1.30       */
    1.31 -    private static Component permanentFocusOwner;
    1.32 +    private static Reference<Component> permanentFocusOwner = new WeakReference<>(null);;
    1.33  
    1.34      /**
    1.35       * The Window which is, or contains, the focus owner.
    1.36       */
    1.37 -    private static Window focusedWindow;
    1.38 +    private static Reference<Window> focusedWindow = new WeakReference<>(null);
    1.39  
    1.40      /**
    1.41       * Only a Frame or a Dialog can be the active Window. The native windowing
    1.42 @@ -308,7 +308,7 @@
    1.43       * Window, or the first Frame or Dialog which is an owner of the focused
    1.44       * Window.
    1.45       */
    1.46 -    private static Window activeWindow;
    1.47 +    private static Reference<Window> activeWindow = new WeakReference<>(null);
    1.48  
    1.49      /**
    1.50       * The default FocusTraversalPolicy for all Windows that have no policy of
    1.51 @@ -475,12 +475,13 @@
    1.52       */
    1.53      public Component getFocusOwner() {
    1.54          synchronized (KeyboardFocusManager.class) {
    1.55 -            if (focusOwner == null) {
    1.56 +            final Component owner = focusOwner.get();
    1.57 +            if (owner == null) {
    1.58                  return null;
    1.59              }
    1.60  
    1.61 -            return (focusOwner.appContext == AppContext.getAppContext())
    1.62 -                ? focusOwner
    1.63 +            return (owner.appContext == AppContext.getAppContext())
    1.64 +                ? owner
    1.65                  : null;
    1.66          }
    1.67      }
    1.68 @@ -506,8 +507,9 @@
    1.69       */
    1.70      protected Component getGlobalFocusOwner() throws SecurityException {
    1.71          synchronized (KeyboardFocusManager.class) {
    1.72 +            Component owner = focusOwner.get();
    1.73              if (this == getCurrentKeyboardFocusManager()) {
    1.74 -                return focusOwner;
    1.75 +                return owner;
    1.76              } else {
    1.77                  if (focusLog.isLoggable(PlatformLogger.FINER)) {
    1.78                      focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
    1.79 @@ -557,7 +559,7 @@
    1.80                      return;
    1.81                  }
    1.82  
    1.83 -                KeyboardFocusManager.focusOwner = focusOwner;
    1.84 +                KeyboardFocusManager.focusOwner = new WeakReference<>(focusOwner);
    1.85  
    1.86                  if (focusOwner != null &&
    1.87                      (getCurrentFocusCycleRoot() == null ||
    1.88 @@ -641,13 +643,14 @@
    1.89       */
    1.90      public Component getPermanentFocusOwner() {
    1.91          synchronized (KeyboardFocusManager.class) {
    1.92 -            if (permanentFocusOwner == null) {
    1.93 +            Component permaOwner = permanentFocusOwner.get();
    1.94 +            if (permaOwner == null) {
    1.95                  return null;
    1.96              }
    1.97  
    1.98 -            return (permanentFocusOwner.appContext ==
    1.99 +            return (permaOwner.appContext ==
   1.100                      AppContext.getAppContext())
   1.101 -                ? permanentFocusOwner
   1.102 +                ? permaOwner
   1.103                  : null;
   1.104          }
   1.105      }
   1.106 @@ -675,8 +678,9 @@
   1.107          throws SecurityException
   1.108      {
   1.109          synchronized (KeyboardFocusManager.class) {
   1.110 +            Component permaOwner = permanentFocusOwner.get();
   1.111              if (this == getCurrentKeyboardFocusManager()) {
   1.112 -                return permanentFocusOwner;
   1.113 +                return permaOwner;
   1.114              } else {
   1.115                  if (focusLog.isLoggable(PlatformLogger.FINER)) {
   1.116                      focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
   1.117 @@ -729,7 +733,7 @@
   1.118                      return;
   1.119                  }
   1.120  
   1.121 -                KeyboardFocusManager.permanentFocusOwner = permanentFocusOwner;
   1.122 +                KeyboardFocusManager.permanentFocusOwner = new WeakReference<>(permanentFocusOwner);
   1.123  
   1.124                  KeyboardFocusManager.
   1.125                      setMostRecentFocusOwner(permanentFocusOwner);
   1.126 @@ -756,12 +760,13 @@
   1.127       */
   1.128      public Window getFocusedWindow() {
   1.129          synchronized (KeyboardFocusManager.class) {
   1.130 -            if (focusedWindow == null) {
   1.131 +            Window win = focusedWindow.get();
   1.132 +            if (win == null) {
   1.133                  return null;
   1.134              }
   1.135  
   1.136 -            return (focusedWindow.appContext == AppContext.getAppContext())
   1.137 -                ? focusedWindow
   1.138 +            return (win.appContext == AppContext.getAppContext())
   1.139 +                ? win
   1.140                  : null;
   1.141          }
   1.142      }
   1.143 @@ -783,8 +788,9 @@
   1.144       */
   1.145      protected Window getGlobalFocusedWindow() throws SecurityException {
   1.146          synchronized (KeyboardFocusManager.class) {
   1.147 +            Window win = focusedWindow.get();
   1.148              if (this == getCurrentKeyboardFocusManager()) {
   1.149 -               return focusedWindow;
   1.150 +               return win;
   1.151              } else {
   1.152                  if (focusLog.isLoggable(PlatformLogger.FINER)) {
   1.153                      focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
   1.154 @@ -831,7 +837,7 @@
   1.155                      return;
   1.156                  }
   1.157  
   1.158 -                KeyboardFocusManager.focusedWindow = focusedWindow;
   1.159 +                KeyboardFocusManager.focusedWindow = new WeakReference<>(focusedWindow);
   1.160                  shouldFire = true;
   1.161              }
   1.162          }
   1.163 @@ -857,12 +863,13 @@
   1.164       */
   1.165      public Window getActiveWindow() {
   1.166          synchronized (KeyboardFocusManager.class) {
   1.167 -            if (activeWindow == null) {
   1.168 +            Window active = activeWindow.get();
   1.169 +            if (active == null) {
   1.170                  return null;
   1.171              }
   1.172  
   1.173 -            return (activeWindow.appContext == AppContext.getAppContext())
   1.174 -                ? activeWindow
   1.175 +            return (active.appContext == AppContext.getAppContext())
   1.176 +                ? active
   1.177                  : null;
   1.178          }
   1.179      }
   1.180 @@ -888,7 +895,7 @@
   1.181      protected Window getGlobalActiveWindow() throws SecurityException {
   1.182          synchronized (KeyboardFocusManager.class) {
   1.183              if (this == getCurrentKeyboardFocusManager()) {
   1.184 -               return activeWindow;
   1.185 +               return activeWindow.get();
   1.186              } else {
   1.187                  if (focusLog.isLoggable(PlatformLogger.FINER)) {
   1.188                      focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
   1.189 @@ -936,7 +943,7 @@
   1.190                  return;
   1.191              }
   1.192  
   1.193 -            KeyboardFocusManager.activeWindow = activeWindow;
   1.194 +            KeyboardFocusManager.activeWindow = new WeakReference<>(activeWindow);
   1.195          }
   1.196  
   1.197          firePropertyChange("activeWindow", oldActiveWindow, activeWindow);
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaActiveWindow.java	Thu Sep 29 15:11:10 2011 -0700
     2.3 @@ -0,0 +1,35 @@
     2.4 +/*
     2.5 +  @test
     2.6 +  @bug 7070542
     2.7 +  @summary KeyboardFocusManager.activeWindow leaks references to components
     2.8 +  @library ../../regtesthelpers
     2.9 +  @author Jaroslav Tulach
    2.10 +  @run main LeakViaActiveWindow
    2.11 +*/
    2.12 +
    2.13 +import java.awt.*;
    2.14 +import java.lang.ref.Reference;
    2.15 +import java.lang.ref.WeakReference;
    2.16 +import javax.swing.JFrame;
    2.17 +
    2.18 +public class LeakViaActiveWindow {
    2.19 +    public static void main(String []s) {
    2.20 +        LeakViaFocusOwner.AccessibleKeyboarFocusManager access = new LeakViaFocusOwner.AccessibleKeyboarFocusManager();
    2.21 +        KeyboardFocusManager currentKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    2.22 +        
    2.23 +        JFrame frame = new JFrame();
    2.24 +        access.setActiveWindow(frame);
    2.25 +        
    2.26 +        if (frame != currentKFM.getActiveWindow()) {
    2.27 +            throw new RuntimeException("Our frame is not focus owner: " + currentKFM.getActiveWindow());
    2.28 +        }
    2.29 +
    2.30 +        Reference<Object> ref = new WeakReference<Object>(frame);
    2.31 +        frame = null;
    2.32 +        LeakViaFocusOwner.assertGC("frame can disappear", ref);
    2.33 +        
    2.34 +        if (currentKFM.getActiveWindow() != null) {
    2.35 +            throw new RuntimeException("Now the owner is GC and thus should be null: " + currentKFM.getActiveWindow());
    2.36 +        }
    2.37 +    }
    2.38 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaFocusOwner.java	Thu Sep 29 15:11:10 2011 -0700
     3.3 @@ -0,0 +1,147 @@
     3.4 +/*
     3.5 +  @test
     3.6 +  @bug 7070542
     3.7 +  @summary KeyboardFocusManager.focusOwner leaks references to components
     3.8 +  @library ../../regtesthelpers
     3.9 +  @author Jaroslav Tulach
    3.10 +  @run main LeakViaFocusOwner
    3.11 +*/
    3.12 +
    3.13 +import java.awt.*;
    3.14 +import java.awt.event.KeyEvent;
    3.15 +import java.lang.ref.Reference;
    3.16 +import java.lang.ref.WeakReference;
    3.17 +import javax.swing.JButton;
    3.18 +import java.util.List;
    3.19 +import java.util.ArrayList;
    3.20 +
    3.21 +public class LeakViaFocusOwner {
    3.22 +    public static void main(String []s) {
    3.23 +        runTestAWT();
    3.24 +    }
    3.25 +
    3.26 +    private static void runTestAWT(){
    3.27 +        AccessibleKeyboarFocusManager access = new AccessibleKeyboarFocusManager();
    3.28 +        KeyboardFocusManager currentKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    3.29 +        
    3.30 +        JButton button = new JButton();
    3.31 +        access.setFocusOwner(button);
    3.32 +        
    3.33 +        if (button != currentKFM.getFocusOwner()) {
    3.34 +            throw new RuntimeException("Our button is not focus owner: " + currentKFM.getFocusOwner());
    3.35 +        }
    3.36 +
    3.37 +        Reference<Object> ref = new WeakReference<Object>(button);
    3.38 +        button = null;
    3.39 +        assertGC("Button can disappear", ref);
    3.40 +        
    3.41 +        if (currentKFM.getFocusOwner() != null) {
    3.42 +            throw new RuntimeException("Now the owner is GC and thus should be null: " + currentKFM.getFocusOwner());
    3.43 +        }
    3.44 +    }
    3.45 +    
    3.46 +    public static void assertGC(final String text, final Reference<?> ref) {
    3.47 +        List<byte[]> alloc = new ArrayList<byte[]>();
    3.48 +        int size = 100000;
    3.49 +        for (int i = 0; i < 50; i++) {
    3.50 +            if (ref.get() == null) {
    3.51 +                return;
    3.52 +            }
    3.53 +            try {
    3.54 +                System.gc();
    3.55 +            } catch (OutOfMemoryError error) {
    3.56 +                // OK
    3.57 +            }
    3.58 +            try {
    3.59 +                System.runFinalization();
    3.60 +            } catch (OutOfMemoryError error) {
    3.61 +                // OK
    3.62 +            }
    3.63 +            try {
    3.64 +                alloc.add(new byte[size]);
    3.65 +                size = (int) (((double) size) * 1.3);
    3.66 +            } catch (OutOfMemoryError error) {
    3.67 +                size = size / 2;
    3.68 +            }
    3.69 +            try {
    3.70 +                if (i % 3 == 0) {
    3.71 +                    Thread.sleep(321);
    3.72 +                }
    3.73 +            } catch (InterruptedException t) {
    3.74 +                // ignore
    3.75 +            }
    3.76 +        }
    3.77 +        alloc = null;
    3.78 +        throw new IllegalStateException(text);
    3.79 +    }
    3.80 +  
    3.81 +    public static final class AccessibleKeyboarFocusManager extends KeyboardFocusManager {
    3.82 +        public void setFocusOwner(Component c) {
    3.83 +            super.setGlobalFocusOwner(c);
    3.84 +        }
    3.85 +        
    3.86 +        public void setFocusedWindow(Window w) {
    3.87 +            super.setGlobalFocusedWindow(w);
    3.88 +        }
    3.89 +        
    3.90 +        public void setActiveWindow(Window w) {
    3.91 +            super.setGlobalActiveWindow(w);
    3.92 +        }
    3.93 +        
    3.94 +        @Override
    3.95 +        public boolean dispatchEvent(AWTEvent e) {
    3.96 +            throw new UnsupportedOperationException("Not supported yet.");
    3.97 +        }
    3.98 +
    3.99 +        @Override
   3.100 +        public boolean dispatchKeyEvent(KeyEvent e) {
   3.101 +            throw new UnsupportedOperationException("Not supported yet.");
   3.102 +        }
   3.103 +
   3.104 +        @Override
   3.105 +        public boolean postProcessKeyEvent(KeyEvent e) {
   3.106 +            throw new UnsupportedOperationException("Not supported yet.");
   3.107 +        }
   3.108 +
   3.109 +        @Override
   3.110 +        public void processKeyEvent(Component focusedComponent, KeyEvent e) {
   3.111 +            throw new UnsupportedOperationException("Not supported yet.");
   3.112 +        }
   3.113 +
   3.114 +        @Override
   3.115 +        protected void enqueueKeyEvents(long after, Component untilFocused) {
   3.116 +            throw new UnsupportedOperationException("Not supported yet.");
   3.117 +        }
   3.118 +
   3.119 +        @Override
   3.120 +        protected void dequeueKeyEvents(long after, Component untilFocused) {
   3.121 +            throw new UnsupportedOperationException("Not supported yet.");
   3.122 +        }
   3.123 +
   3.124 +        @Override
   3.125 +        protected void discardKeyEvents(Component comp) {
   3.126 +            throw new UnsupportedOperationException("Not supported yet.");
   3.127 +        }
   3.128 +
   3.129 +        @Override
   3.130 +        public void focusNextComponent(Component aComponent) {
   3.131 +            throw new UnsupportedOperationException("Not supported yet.");
   3.132 +        }
   3.133 +
   3.134 +        @Override
   3.135 +        public void focusPreviousComponent(Component aComponent) {
   3.136 +            throw new UnsupportedOperationException("Not supported yet.");
   3.137 +        }
   3.138 +
   3.139 +        @Override
   3.140 +        public void upFocusCycle(Component aComponent) {
   3.141 +            throw new UnsupportedOperationException("Not supported yet.");
   3.142 +        }
   3.143 +
   3.144 +        @Override
   3.145 +        public void downFocusCycle(Container aContainer) {
   3.146 +            throw new UnsupportedOperationException("Not supported yet.");
   3.147 +        }
   3.148 +        
   3.149 +    }
   3.150 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/test/java/awt/KeyboardFocusmanager/MemoryLeaks/LeakViaFocusedWindow.java	Thu Sep 29 15:11:10 2011 -0700
     4.3 @@ -0,0 +1,35 @@
     4.4 +/*
     4.5 +  @test
     4.6 +  @bug 7070542
     4.7 +  @summary KeyboardFocusManager.focusedWindow leaks references to components
     4.8 +  @library ../../regtesthelpers
     4.9 +  @author Jaroslav Tulach
    4.10 +  @run main LeakViaFocusedWindow
    4.11 +*/
    4.12 +
    4.13 +import java.awt.*;
    4.14 +import java.lang.ref.Reference;
    4.15 +import java.lang.ref.WeakReference;
    4.16 +import javax.swing.JFrame;
    4.17 +
    4.18 +public class LeakViaFocusedWindow {
    4.19 +    public static void main(String []s) {
    4.20 +        LeakViaFocusOwner.AccessibleKeyboarFocusManager access = new LeakViaFocusOwner.AccessibleKeyboarFocusManager();
    4.21 +        KeyboardFocusManager currentKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    4.22 +        
    4.23 +        JFrame frame = new JFrame();
    4.24 +        access.setFocusedWindow(frame);
    4.25 +        
    4.26 +        if (frame != currentKFM.getFocusedWindow()) {
    4.27 +            throw new RuntimeException("Our frame is not focus owner: " + currentKFM.getFocusedWindow());
    4.28 +        }
    4.29 +
    4.30 +        Reference<Object> ref = new WeakReference<Object>(frame);
    4.31 +        frame = null;
    4.32 +        LeakViaFocusOwner.assertGC("frame can disappear", ref);
    4.33 +        
    4.34 +        if (currentKFM.getFocusedWindow() != null) {
    4.35 +            throw new RuntimeException("Now the owner is GC and thus should be null: " + currentKFM.getFocusedWindow());
    4.36 +        }
    4.37 +    }
    4.38 +}