6936389: FontManager.fileCloser may cause memory leak in applets
authorbae
Tue, 20 Apr 2010 11:06:47 +0400
changeset 5275 fa9f5ce2006e
parent 5223 44158f6d3b94
child 5276 3c1c9fda17a1
6936389: FontManager.fileCloser may cause memory leak in applets Reviewed-by: igor, prr, ant
jdk/src/share/classes/sun/font/SunFontManager.java
jdk/src/share/classes/sun/java2d/Disposer.java
jdk/src/share/classes/sun/java2d/loops/GraphicsPrimitive.java
jdk/src/solaris/classes/sun/awt/X11/XToolkit.java
jdk/src/solaris/classes/sun/awt/X11GraphicsDevice.java
jdk/src/solaris/classes/sun/awt/motif/MToolkit.java
jdk/src/windows/classes/sun/awt/windows/WToolkit.java
jdk/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java
jdk/test/sun/awt/font/ClassLoaderLeakTest.java
--- a/jdk/src/share/classes/sun/font/SunFontManager.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/share/classes/sun/font/SunFontManager.java	Tue Apr 20 11:06:47 2010 +0400
@@ -2310,6 +2310,7 @@
                                    tgn != null;
                                    tg = tgn, tgn = tg.getParent());
                               fileCloser = new Thread(tg, fileCloserRunnable);
+                              fileCloser.setContextClassLoader(null);
                               Runtime.getRuntime().addShutdownHook(fileCloser);
                               return null;
                           }
--- a/jdk/src/share/classes/sun/java2d/Disposer.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/Disposer.java	Tue Apr 20 11:06:47 2010 +0400
@@ -84,6 +84,7 @@
                          tg = tgn, tgn = tg.getParent());
                     Thread t =
                         new Thread(tg, disposerInstance, "Java2D Disposer");
+                    t.setContextClassLoader(null);
                     t.setDaemon(true);
                     t.setPriority(Thread.MAX_PRIORITY);
                     t.start();
--- a/jdk/src/share/classes/sun/java2d/loops/GraphicsPrimitive.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/loops/GraphicsPrimitive.java	Tue Apr 20 11:06:47 2010 +0400
@@ -417,7 +417,9 @@
         public static void setShutdownHook() {
             AccessController.doPrivileged(new PrivilegedAction() {
                 public Object run() {
-                    Runtime.getRuntime().addShutdownHook(new TraceReporter());
+                    TraceReporter t = new TraceReporter();
+                    t.setContextClassLoader(null);
+                    Runtime.getRuntime().addShutdownHook(t);
                     return null;
                 }
             });
--- a/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java	Tue Apr 20 11:06:47 2010 +0400
@@ -307,22 +307,35 @@
         } finally {
             awtUnlock();
         }
-
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            public void run() {
-                XSystemTrayPeer peer = XSystemTrayPeer.getPeerInstance();
-                if (peer != null) {
-                    peer.dispose();
+        PrivilegedAction<Void> a = new PrivilegedAction<Void>() {
+            public Void run() {
+                ThreadGroup mainTG = Thread.currentThread().getThreadGroup();
+                ThreadGroup parentTG = mainTG.getParent();
+                while (parentTG != null) {
+                    mainTG = parentTG;
+                    parentTG = mainTG.getParent();
                 }
-                if (xs != null) {
-                    ((XAWTXSettings)xs).dispose();
-                }
-                freeXKB();
-                if (log.isLoggable(PlatformLogger.FINE)) {
-                    dumpPeers();
-                }
+                Thread shutdownThread = new Thread(mainTG, "XToolkt-Shutdown-Thread") {
+                        public void run() {
+                            XSystemTrayPeer peer = XSystemTrayPeer.getPeerInstance();
+                            if (peer != null) {
+                                peer.dispose();
+                            }
+                            if (xs != null) {
+                                ((XAWTXSettings)xs).dispose();
+                            }
+                            freeXKB();
+                            if (log.isLoggable(PlatformLogger.FINE)) {
+                                dumpPeers();
+                            }
+                        }
+                    };
+                shutdownThread.setContextClassLoader(null);
+                Runtime.getRuntime().addShutdownHook(shutdownThread);
+                return null;
             }
-        });
+        };
+        AccessController.doPrivileged(a);
     }
 
     static String getCorrectXIDString(String val) {
--- a/jdk/src/solaris/classes/sun/awt/X11GraphicsDevice.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/solaris/classes/sun/awt/X11GraphicsDevice.java	Tue Apr 20 11:06:47 2010 +0400
@@ -32,6 +32,8 @@
 import java.awt.GraphicsConfiguration;
 import java.awt.Rectangle;
 import java.awt.Window;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.HashMap;
@@ -402,17 +404,30 @@
             // is already in the original DisplayMode at that time, this
             // hook will have no effect)
             shutdownHookRegistered = true;
-            Runnable r = new Runnable() {
-                public void run() {
-                    Window old = getFullScreenWindow();
-                    if (old != null) {
-                        exitFullScreenExclusive(old);
-                        setDisplayMode(origDisplayMode);
+            PrivilegedAction<Void> a = new PrivilegedAction<Void>() {
+                public Void run() {
+                    ThreadGroup mainTG = Thread.currentThread().getThreadGroup();
+                    ThreadGroup parentTG = mainTG.getParent();
+                    while (parentTG != null) {
+                        mainTG = parentTG;
+                        parentTG = mainTG.getParent();
                     }
+                    Runnable r = new Runnable() {
+                            public void run() {
+                                Window old = getFullScreenWindow();
+                                if (old != null) {
+                                    exitFullScreenExclusive(old);
+                                    setDisplayMode(origDisplayMode);
+                                }
+                            }
+                        };
+                    Thread t = new Thread(mainTG, r,"Display-Change-Shutdown-Thread-"+screen);
+                    t.setContextClassLoader(null);
+                    Runtime.getRuntime().addShutdownHook(t);
+                    return null;
                 }
             };
-            Thread t = new Thread(r,"Display-Change-Shutdown-Thread-"+screen);
-            Runtime.getRuntime().addShutdownHook(t);
+            AccessController.doPrivileged(a);
         }
 
         // switch to the new DisplayMode
--- a/jdk/src/solaris/classes/sun/awt/motif/MToolkit.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/solaris/classes/sun/awt/motif/MToolkit.java	Tue Apr 20 11:06:47 2010 +0400
@@ -156,27 +156,27 @@
             Thread toolkitThread = new Thread(this, "AWT-Motif");
             toolkitThread.setPriority(Thread.NORM_PRIORITY + 1);
             toolkitThread.setDaemon(true);
-            ThreadGroup mainTG = (ThreadGroup)AccessController.doPrivileged(
-                new PrivilegedAction() {
-                    public Object run() {
-                        ThreadGroup currentTG =
-                            Thread.currentThread().getThreadGroup();
-                        ThreadGroup parentTG = currentTG.getParent();
-                        while (parentTG != null) {
-                            currentTG = parentTG;
-                            parentTG = currentTG.getParent();
-                        }
-                        return currentTG;
+
+            PrivilegedAction<Void> a = new PrivilegedAction<Void>() {
+                public Void run() {
+                    ThreadGroup mainTG = Thread.currentThread().getThreadGroup();
+                    ThreadGroup parentTG = mainTG.getParent();
+
+                    while (parentTG != null) {
+                        mainTG = parentTG;
+                        parentTG = mainTG.getParent();
                     }
-            });
-
-            Runtime.getRuntime().addShutdownHook(
-                new Thread(mainTG, new Runnable() {
-                    public void run() {
-                        shutdown();
-                    }
-                }, "Shutdown-Thread")
-            );
+                    Thread shutdownThread = new Thread(mainTG, new Runnable() {
+                            public void run() {
+                                shutdown();
+                            }
+                        }, "Shutdown-Thread");
+                    shutdownThread.setContextClassLoader(null);
+                    Runtime.getRuntime().addShutdownHook(shutdownThread);
+                    return null;
+                }
+            };
+            AccessController.doPrivileged(a);
 
             /*
              * Fix for 4701990.
--- a/jdk/src/windows/classes/sun/awt/windows/WToolkit.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/windows/classes/sun/awt/windows/WToolkit.java	Tue Apr 20 11:06:47 2010 +0400
@@ -266,27 +266,25 @@
         boolean startPump = init();
 
         if (startPump) {
-            ThreadGroup mainTG = (ThreadGroup)AccessController.doPrivileged(
-                new PrivilegedAction() {
-                    public Object run() {
-                        ThreadGroup currentTG =
-                            Thread.currentThread().getThreadGroup();
-                        ThreadGroup parentTG = currentTG.getParent();
-                        while (parentTG != null) {
-                            currentTG = parentTG;
-                            parentTG = currentTG.getParent();
-                        }
-                        return currentTG;
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ThreadGroup currentTG =
+                        Thread.currentThread().getThreadGroup();
+                    ThreadGroup parentTG = currentTG.getParent();
+                    while (parentTG != null) {
+                        currentTG = parentTG;
+                        parentTG = currentTG.getParent();
                     }
+                    Thread shutdown = new Thread(currentTG, new Runnable() {
+                            public void run() {
+                                shutdown();
+                            }
+                        });
+                    shutdown.setContextClassLoader(null);
+                    Runtime.getRuntime().addShutdownHook(shutdown);
+                    return null;
+                }
             });
-
-            Runtime.getRuntime().addShutdownHook(
-                new Thread(mainTG, new Runnable() {
-                    public void run() {
-                        shutdown();
-                    }
-                })
-            );
         }
 
         synchronized(this) {
--- a/jdk/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java	Wed Jul 05 17:09:51 2017 +0200
+++ b/jdk/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java	Tue Apr 20 11:06:47 2010 +0400
@@ -101,17 +101,15 @@
                         currentTG = parentTG;
                         parentTG = currentTG.getParent();
                     }
+                    Thread shutdown = new Thread(currentTG, new Runnable() {
+                            public void run() {
+                                done = true;
+                                wakeUpUpdateThread();
+                            }
+                        });
+                    shutdown.setContextClassLoader(null);
                     try {
-                        Runtime.getRuntime().addShutdownHook(
-                            new Thread(currentTG,
-                                new Runnable() {
-                                    public void run() {
-                                        done = true;
-                                        wakeUpUpdateThread();
-                                    }
-                                }
-                            )
-                        );
+                        Runtime.getRuntime().addShutdownHook(shutdown);
                     } catch (Exception e) {
                         done = true;
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/awt/font/ClassLoaderLeakTest.java	Tue Apr 20 11:06:47 2010 +0400
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+/**
+ * @test
+ * @bug     6936389
+ *
+ * @summary Test verifes that LogManager shutdown hook does not cause
+ *          an application classloader leaks.
+ *
+ * @run     main/othervm ClassLoaderLeakTest FontManagerTest
+ */
+
+import java.awt.Font;
+import java.awt.Graphics;
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.concurrent.CountDownLatch;
+
+public class ClassLoaderLeakTest {
+
+    private static CountDownLatch doneSignal;
+    private static CountDownLatch launchSignal;
+    private static Throwable launchFailure = null;
+
+    public static void main(String[] args) {
+        doneSignal = new CountDownLatch(1);
+        launchSignal = new CountDownLatch(1);
+
+        String testcase = "FontManagerTest";
+
+        if (args.length > 0) {
+            testcase = args[0];
+        }
+
+        /* prepare test  class loader */
+        URL pwd = null;
+        try {
+
+            pwd = new File(System.getProperty("test.classes", ".")).toURL();
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Test failed.", e);
+        }
+        System.out.println("PWD: " + pwd);
+        URL[] urls = new URL[]{pwd};
+
+        MyClassLoader appClassLoader = new MyClassLoader(urls, "test0");
+        WeakReference<MyClassLoader> ref =
+            new WeakReference<MyClassLoader>(appClassLoader);
+
+        ThreadGroup appsThreadGroup = new ThreadGroup("MyAppsThreadGroup");
+
+        Runnable launcher = new TestLauncher(testcase);
+
+        Thread appThread = new Thread(appsThreadGroup, launcher, "AppThread-0");
+        appThread.setContextClassLoader(appClassLoader);
+
+        appThread.start();
+        appsThreadGroup = null;
+        appClassLoader = null;
+        launcher = null;
+        appThread = null;
+
+        /* wait for laucnh completion */
+        try {
+            launchSignal.await();
+        } catch (InterruptedException e) {
+        }
+
+        /* check if launch failed */
+        if (launchFailure != null) {
+            throw new RuntimeException("Test failed.", launchFailure);
+        }
+
+        /* wait for test app excution completion */
+        try {
+            doneSignal.await();
+        } catch (InterruptedException e) {
+        }
+
+        /* give a chance to GC */
+        waitAndGC(9);
+
+        if (ref.get() != null) {
+            throw new RuntimeException("Test failed: classloader is still alive");
+        }
+
+
+        System.out.println("Test passed.");
+    }
+
+    private static class TestLauncher implements Runnable {
+
+        private String className;
+
+        public TestLauncher(String name) {
+            className = name;
+        }
+
+        public void run() {
+            try {
+                ClassLoader cl =
+                    Thread.currentThread().getContextClassLoader();
+                Class appMain = cl.loadClass(className);
+                Method launch =
+                    appMain.getMethod("launch", doneSignal.getClass());
+
+                Constructor c = appMain.getConstructor();
+
+                Object o = c.newInstance();
+
+                launch.invoke(o, doneSignal);
+
+            } catch (Throwable e) {
+                launchFailure = e;
+            } finally {
+                launchSignal.countDown();
+            }
+        }
+    }
+
+    private static class MyClassLoader extends URLClassLoader {
+
+        private static boolean verbose =
+            Boolean.getBoolean("verboseClassLoading");
+        private String uniqClassName;
+
+        public MyClassLoader(URL[] urls, String uniq) {
+            super(urls);
+
+            uniqClassName = uniq;
+        }
+
+        public Class loadClass(String name) throws ClassNotFoundException {
+            if (verbose) {
+                System.out.printf("%s: load class %s\n", uniqClassName, name);
+            }
+            if (uniqClassName.equals(name)) {
+                return Object.class;
+            }
+            return super.loadClass(name);
+        }
+
+        public String toString() {
+            return "MyClassLoader(" + uniqClassName + ")";
+        }
+    }
+
+    private static void waitAndGC(int sec) {
+        int cnt = sec;
+        System.out.print("Wait ");
+        while (cnt-- > 0) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+            // do GC every 3 seconds
+            if (cnt % 3 == 2) {
+                System.gc();
+                System.out.print("+");
+            } else {
+                System.out.print(".");
+            }
+            //checkErrors();
+        }
+        System.out.println("");
+    }
+}
+
+abstract class AppTest {
+
+    public AppTest() {
+    }
+
+    protected abstract void doTest();
+
+    public void launch(CountDownLatch done) {
+        System.out.println("Testcase: " + this.getClass().getName());
+        try {
+            doTest();
+        } finally {
+            done.countDown();
+        }
+    }
+}
+
+class FontManagerTest extends AppTest {
+
+    public FontManagerTest() {
+    }
+
+    protected void doTest() {
+        Font f = new Font(Font.SANS_SERIF, Font.ITALIC, 24);
+        f.getNumGlyphs();
+    }
+}