# HG changeset patch # User andrew # Date 1365712268 -3600 # Node ID 4d66f7ebcf99c1b322f47ff0aa6adadcd995f8f4 # Parent df591e0dfd349dc5986cc17949939c588d5a9690 7064279: Introspector.getBeanInfo() should release some resources in timely manner Reviewed-by: art, alexp diff --git a/src/share/classes/java/beans/Beans.java b/src/share/classes/java/beans/Beans.java --- jdk/src/share/classes/java/beans/Beans.java +++ jdk/src/share/classes/java/beans/Beans.java @@ -32,7 +32,6 @@ import java.applet.AppletStub; import java.applet.AudioClip; -import java.awt.GraphicsEnvironment; import java.awt.Image; import java.beans.beancontext.BeanContext; @@ -53,15 +52,11 @@ import java.util.Iterator; import java.util.Vector; -import sun.awt.AppContext; - /** * This class provides some general purpose beans control methods. */ public class Beans { - private static final Object DESIGN_TIME = new Object(); - private static final Object GUI_AVAILABLE = new Object(); /** *

@@ -395,8 +390,7 @@ * @see DesignMode */ public static boolean isDesignTime() { - Object value = AppContext.getAppContext().get(DESIGN_TIME); - return (value instanceof Boolean) && (Boolean) value; + return ThreadGroupContext.getContext().isDesignTime(); } /** @@ -413,8 +407,7 @@ * */ public static boolean isGuiAvailable() { - Object value = AppContext.getAppContext().get(GUI_AVAILABLE); - return (value instanceof Boolean) ? (Boolean) value : !GraphicsEnvironment.isHeadless(); + return ThreadGroupContext.getContext().isGuiAvailable(); } /** @@ -440,7 +433,7 @@ if (sm != null) { sm.checkPropertiesAccess(); } - AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime)); + ThreadGroupContext.getContext().setDesignTime(isDesignTime); } /** @@ -466,7 +459,7 @@ if (sm != null) { sm.checkPropertiesAccess(); } - AppContext.getAppContext().put(GUI_AVAILABLE, Boolean.valueOf(isGuiAvailable)); + ThreadGroupContext.getContext().setGuiAvailable(isGuiAvailable); } } diff --git a/src/share/classes/java/beans/Introspector.java b/src/share/classes/java/beans/Introspector.java --- jdk/src/share/classes/java/beans/Introspector.java +++ jdk/src/share/classes/java/beans/Introspector.java @@ -38,7 +38,6 @@ import java.util.EventListener; import java.util.List; import java.util.TreeMap; -import sun.awt.AppContext; import sun.reflect.misc.ReflectUtil; /** @@ -103,10 +102,7 @@ public final static int IGNORE_ALL_BEANINFO = 3; // Static Caches to speed up introspection. - private static WeakCache, Method[]> declaredMethodCache = - new WeakCache, Method[]>(); - - private static final Object BEANINFO_CACHE = new Object(); + private static final WeakCache, Method[]> declaredMethodCache = new WeakCache<>(); private Class beanClass; private BeanInfo explicitBeanInfo; @@ -170,21 +166,15 @@ if (!ReflectUtil.isPackageAccessible(beanClass)) { return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); } - WeakCache, BeanInfo> beanInfoCache; + ThreadGroupContext context = ThreadGroupContext.getContext(); BeanInfo beanInfo; - synchronized (BEANINFO_CACHE) { - beanInfoCache = (WeakCache, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); - - if (beanInfoCache == null) { - beanInfoCache = new WeakCache, BeanInfo>(); - AppContext.getAppContext().put(BEANINFO_CACHE, beanInfoCache); - } - beanInfo = beanInfoCache.get(beanClass); + synchronized (declaredMethodCache) { + beanInfo = context.getBeanInfo(beanClass); } if (beanInfo == null) { beanInfo = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); - synchronized (BEANINFO_CACHE) { - beanInfoCache.put(beanClass, beanInfo); + synchronized (declaredMethodCache) { + context.putBeanInfo(beanClass, beanInfo); } } return beanInfo; @@ -334,11 +324,8 @@ */ public static void flushCaches() { - synchronized (BEANINFO_CACHE) { - WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.clear(); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().clearBeanInfoCache(); declaredMethodCache.clear(); } } @@ -362,11 +349,8 @@ if (clz == null) { throw new NullPointerException(); } - synchronized (BEANINFO_CACHE) { - WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.put(clz, null); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().removeBeanInfo(clz); declaredMethodCache.put(clz, null); } } @@ -1313,7 +1297,7 @@ if (!ReflectUtil.isPackageAccessible(clz)) { return new Method[0]; } - synchronized (BEANINFO_CACHE) { + synchronized (declaredMethodCache) { Method[] result = declaredMethodCache.get(clz); if (result == null) { result = clz.getMethods(); diff --git a/src/share/classes/java/beans/ThreadGroupContext.java b/src/share/classes/java/beans/ThreadGroupContext.java new file mode 100644 --- /dev/null +++ jdk/src/share/classes/java/beans/ThreadGroupContext.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +import com.sun.beans.finder.BeanInfoFinder; +import com.sun.beans.finder.PropertyEditorFinder; + +import java.awt.GraphicsEnvironment; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * The {@code ThreadGroupContext} is an application-dependent + * context referenced by the specific {@link ThreadGroup}. + * This is a replacement for the {@link sun.awt.AppContext}. + * + * @author Sergey Malenkov + */ +final class ThreadGroupContext { + + private static final Map contexts = new WeakHashMap<>(); + + /** + * Returns the appropriate {@code AppContext} for the caller, + * as determined by its {@code ThreadGroup}. + * + * @return the application-dependent context + */ + static ThreadGroupContext getContext() { + ThreadGroup group = Thread.currentThread().getThreadGroup(); + synchronized (contexts) { + ThreadGroupContext context = contexts.get(group); + if (context == null) { + context = new ThreadGroupContext(); + contexts.put(group, context); + } + return context; + } + } + + private volatile boolean isDesignTime; + private volatile Boolean isGuiAvailable; + + private Map, BeanInfo> beanInfoCache; + private BeanInfoFinder beanInfoFinder; + private PropertyEditorFinder propertyEditorFinder; + + + boolean isDesignTime() { + return this.isDesignTime; + } + + void setDesignTime(boolean isDesignTime) { + this.isDesignTime = isDesignTime; + } + + + boolean isGuiAvailable() { + Boolean isGuiAvailable = this.isGuiAvailable; + return (isGuiAvailable != null) + ? isGuiAvailable.booleanValue() + : !GraphicsEnvironment.isHeadless(); + } + + void setGuiAvailable(boolean isGuiAvailable) { + this.isGuiAvailable = Boolean.valueOf(isGuiAvailable); + } + + + BeanInfo getBeanInfo(Class type) { + return (this.beanInfoCache != null) + ? this.beanInfoCache.get(type) + : null; + } + + BeanInfo putBeanInfo(Class type, BeanInfo info) { + if (this.beanInfoCache == null) { + this.beanInfoCache = new WeakHashMap<>(); + } + return this.beanInfoCache.put(type, info); + } + + void removeBeanInfo(Class type) { + if (this.beanInfoCache != null) { + this.beanInfoCache.remove(type); + } + } + + void clearBeanInfoCache() { + if (this.beanInfoCache != null) { + this.beanInfoCache.clear(); + } + } + + + synchronized BeanInfoFinder getBeanInfoFinder() { + if (this.beanInfoFinder == null) { + this.beanInfoFinder = new BeanInfoFinder(); + } + return this.beanInfoFinder; + } + + synchronized PropertyEditorFinder getPropertyEditorFinder() { + if (this.propertyEditorFinder == null) { + this.propertyEditorFinder = new PropertyEditorFinder(); + } + return this.propertyEditorFinder; + } +} diff --git a/test/java/beans/Beans/6669869/TestDesignTime.java b/test/java/beans/Beans/6669869/TestDesignTime.java --- jdk/test/java/beans/Beans/6669869/TestDesignTime.java +++ jdk/test/java/beans/Beans/6669869/TestDesignTime.java @@ -29,7 +29,6 @@ */ import java.beans.Beans; -import sun.awt.SunToolkit; public class TestDesignTime implements Runnable { public static void main(String[] args) throws InterruptedException { @@ -44,7 +43,6 @@ } public void run() { - SunToolkit.createNewAppContext(); if (Beans.isDesignTime()) { throw new Error("shared DesignTime property"); } diff --git a/test/java/beans/Beans/6669869/TestGuiAvailable.java b/test/java/beans/Beans/6669869/TestGuiAvailable.java --- jdk/test/java/beans/Beans/6669869/TestGuiAvailable.java +++ jdk/test/java/beans/Beans/6669869/TestGuiAvailable.java @@ -30,7 +30,6 @@ import java.awt.GraphicsEnvironment; import java.beans.Beans; -import sun.awt.SunToolkit; public class TestGuiAvailable implements Runnable { public static void main(String[] args) throws InterruptedException { @@ -45,7 +44,6 @@ } public void run() { - SunToolkit.createNewAppContext(); if (Beans.isGuiAvailable() == GraphicsEnvironment.isHeadless()) { throw new Error("shared GuiAvailable property"); } diff --git a/test/java/beans/Introspector/7064279/Test7064279.java b/test/java/beans/Introspector/7064279/Test7064279.java new file mode 100644 --- /dev/null +++ jdk/test/java/beans/Introspector/7064279/Test7064279.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 7064279 + * @summary Tests that Introspector does not have strong references to context class loader + * @author Sergey Malenkov + */ + +import java.beans.Introspector; +import java.io.File; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; + +public class Test7064279 { + + public static void main(String[] args) throws Exception { + WeakReference ref = new WeakReference(test("test.jar", "test.Test")); + try { + int[] array = new int[1024]; + while (true) { + array = new int[array.length << 1]; + } + } + catch (OutOfMemoryError error) { + System.gc(); + } + if (null != ref.get()) { + throw new Error("ClassLoader is not released"); + } + } + + private static Object test(String jarName, String className) throws Exception { + StringBuilder sb = new StringBuilder(256); + sb.append("file:"); + sb.append(System.getProperty("test.src", ".")); + sb.append(File.separatorChar); + sb.append(jarName); + + ClassLoader newLoader = new URLClassLoader(new URL[] { new URL(sb.toString()) }); + ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader(newLoader); + test(newLoader.loadClass(className)); + Thread.currentThread().setContextClassLoader(oldLoader); + + return newLoader; + } + + private static void test(Class type) throws Exception { + Introspector.getBeanInfo(type); + } +} diff --git a/test/java/beans/Introspector/Test6660539.java b/test/java/beans/Introspector/Test6660539.java --- jdk/test/java/beans/Introspector/Test6660539.java +++ jdk/test/java/beans/Introspector/Test6660539.java @@ -28,8 +28,6 @@ * @author Sergey Malenkov */ -import sun.awt.SunToolkit; - import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; @@ -49,7 +47,6 @@ } public void run() { - SunToolkit.createNewAppContext(); for (PropertyDescriptor pd : getPropertyDescriptors()) { if (pd.getDisplayName().equals(NAME)) throw new Error("shared BeanInfo cache");