6936389: FontManager.fileCloser may cause memory leak in applets
Reviewed-by: igor, prr, ant
--- 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();
+ }
+}