From b2a2c0999a39184bef1b841e9075098de3987382 Mon Sep 17 00:00:00 2001 From: malenkov Date: Thu, 10 Nov 2011 17:27:40 +0400 Subject: [PATCH] 7064279: Introspector.getBeanInfo() should release some resources in timely manner Reviewed-by: art, alexp --- src/share/classes/java/beans/Beans.java | 15 +- .../classes/java/beans/Introspector.java | 58 ++------ .../java/beans/PropertyEditorManager.java | 24 +--- .../java/beans/ThreadGroupContext.java | 133 ++++++++++++++++++ .../beans/Beans/6669869/TestDesignTime.java | 2 - .../beans/Beans/6669869/TestGuiAvailable.java | 2 - .../Introspector/6380849/TestBeanInfo.java | 7 - .../Introspector/7064279/Test7064279.java | 75 ++++++++++ test/java/beans/Introspector/7064279/test.jar | Bin 0 -> 1573 bytes test/java/beans/Introspector/Test6660539.java | 3 - .../6380849/TestPropertyEditor.java | 8 -- 11 files changed, 230 insertions(+), 97 deletions(-) create mode 100644 src/share/classes/java/beans/ThreadGroupContext.java create mode 100644 test/java/beans/Introspector/7064279/Test7064279.java create mode 100644 test/java/beans/Introspector/7064279/test.jar diff --git a/src/share/classes/java/beans/Beans.java b/src/share/classes/java/beans/Beans.java index b71a2ac5a..03f0dc8d7 100644 --- a/src/share/classes/java/beans/Beans.java +++ b/src/share/classes/java/beans/Beans.java @@ -32,7 +32,6 @@ import java.applet.AppletContext; 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.Hashtable; 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 @@ public class Beans { * @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 class Beans { * */ 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 @@ public class Beans { if (sm != null) { sm.checkPropertiesAccess(); } - AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime)); + ThreadGroupContext.getContext().setDesignTime(isDesignTime); } /** @@ -466,7 +459,7 @@ public class Beans { 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 index 36ead726b..2d85beef4 100644 --- a/src/share/classes/java/beans/Introspector.java +++ b/src/share/classes/java/beans/Introspector.java @@ -26,7 +26,6 @@ package java.beans; import com.sun.beans.WeakCache; -import com.sun.beans.finder.BeanInfoFinder; import com.sun.beans.finder.ClassFinder; import java.awt.Component; @@ -44,9 +43,7 @@ import java.util.EventListener; import java.util.EventObject; import java.util.List; import java.util.TreeMap; -import java.util.WeakHashMap; -import sun.awt.AppContext; import sun.reflect.misc.ReflectUtil; /** @@ -98,10 +95,7 @@ public class Introspector { 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; @@ -134,8 +128,6 @@ public class Introspector { static final String SET_PREFIX = "set"; static final String IS_PREFIX = "is"; - private static final Object FINDER_KEY = new Object(); - //====================================================================== // Public methods //====================================================================== @@ -160,20 +152,15 @@ public class Introspector { if (!ReflectUtil.isPackageAccessible(beanClass)) { return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); } - Map, BeanInfo> beanInfoCache; + ThreadGroupContext context = ThreadGroupContext.getContext(); BeanInfo beanInfo; - synchronized (BEANINFO_CACHE) { - beanInfoCache = (Map, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache == null) { - beanInfoCache = new WeakHashMap, 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; @@ -306,7 +293,7 @@ public class Introspector { */ public static String[] getBeanInfoSearchPath() { - return getFinder().getPackages(); + return ThreadGroupContext.getContext().getBeanInfoFinder().getPackages(); } /** @@ -330,7 +317,7 @@ public class Introspector { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().setPackages(path); + ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path); } @@ -342,11 +329,8 @@ public class Introspector { */ public static void flushCaches() { - synchronized (BEANINFO_CACHE) { - Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.clear(); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().clearBeanInfoCache(); declaredMethodCache.clear(); } } @@ -370,11 +354,8 @@ public class Introspector { if (clz == null) { throw new NullPointerException(); } - synchronized (BEANINFO_CACHE) { - Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.put(clz, null); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().removeBeanInfo(clz); declaredMethodCache.put(clz, null); } } @@ -452,7 +433,7 @@ public class Introspector { * @return Instance of an explicit BeanInfo class or null if one isn't found. */ private static BeanInfo findExplicitBeanInfo(Class beanClass) { - return getFinder().find(beanClass); + return ThreadGroupContext.getContext().getBeanInfoFinder().find(beanClass); } /** @@ -1275,7 +1256,7 @@ public class Introspector { if (!ReflectUtil.isPackageAccessible(clz)) { return new Method[0]; } - synchronized (BEANINFO_CACHE) { + synchronized (declaredMethodCache) { Method[] result = declaredMethodCache.get(clz); if (result == null) { result = clz.getMethods(); @@ -1426,17 +1407,6 @@ public class Introspector { return false; } - private static BeanInfoFinder getFinder() { - AppContext context = AppContext.getAppContext(); - Object object = context.get(FINDER_KEY); - if (object instanceof BeanInfoFinder) { - return (BeanInfoFinder) object; - } - BeanInfoFinder finder = new BeanInfoFinder(); - context.put(FINDER_KEY, finder); - return finder; - } - /** * Try to create an instance of a named class. * First try the classloader of "sibling", then try the system diff --git a/src/share/classes/java/beans/PropertyEditorManager.java b/src/share/classes/java/beans/PropertyEditorManager.java index 6bdfa95f4..3fee3f408 100644 --- a/src/share/classes/java/beans/PropertyEditorManager.java +++ b/src/share/classes/java/beans/PropertyEditorManager.java @@ -25,9 +25,6 @@ package java.beans; -import com.sun.beans.finder.PropertyEditorFinder; -import sun.awt.AppContext; - /** * The PropertyEditorManager can be used to locate a property editor for * any given type name. This property editor must support the @@ -55,8 +52,6 @@ import sun.awt.AppContext; public class PropertyEditorManager { - private static final Object FINDER_KEY = new Object(); - /** * Registers an editor class to edit values of the given target class. * If the editor class is {@code null}, @@ -81,7 +76,7 @@ public class PropertyEditorManager { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().register(targetType, editorClass); + ThreadGroupContext.getContext().getPropertyEditorFinder().register(targetType, editorClass); } /** @@ -92,7 +87,7 @@ public class PropertyEditorManager { * The result is null if no suitable editor can be found. */ public static PropertyEditor findEditor(Class targetType) { - return getFinder().find(targetType); + return ThreadGroupContext.getContext().getPropertyEditorFinder().find(targetType); } /** @@ -104,7 +99,7 @@ public class PropertyEditorManager { * e.g. Sun implementation initially sets to {"sun.beans.editors"}. */ public static String[] getEditorSearchPath() { - return getFinder().getPackages(); + return ThreadGroupContext.getContext().getPropertyEditorFinder().getPackages(); } /** @@ -125,17 +120,6 @@ public class PropertyEditorManager { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().setPackages(path); - } - - private static PropertyEditorFinder getFinder() { - AppContext context = AppContext.getAppContext(); - Object object = context.get(FINDER_KEY); - if (object instanceof PropertyEditorFinder) { - return (PropertyEditorFinder) object; - } - PropertyEditorFinder finder = new PropertyEditorFinder(); - context.put(FINDER_KEY, finder); - return finder; + ThreadGroupContext.getContext().getPropertyEditorFinder().setPackages(path); } } diff --git a/src/share/classes/java/beans/ThreadGroupContext.java b/src/share/classes/java/beans/ThreadGroupContext.java new file mode 100644 index 000000000..dc1d38a14 --- /dev/null +++ b/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 index b73cee4bb..731ab4a9f 100644 --- a/test/java/beans/Beans/6669869/TestDesignTime.java +++ b/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 class TestDesignTime implements Runnable { } 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 index 615688286..67d88ae34 100644 --- a/test/java/beans/Beans/6669869/TestGuiAvailable.java +++ b/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 class TestGuiAvailable implements Runnable { } public void run() { - SunToolkit.createNewAppContext(); if (Beans.isGuiAvailable() == GraphicsEnvironment.isHeadless()) { throw new Error("shared GuiAvailable property"); } diff --git a/test/java/beans/Introspector/6380849/TestBeanInfo.java b/test/java/beans/Introspector/6380849/TestBeanInfo.java index 8232756d7..6d6853504 100644 --- a/test/java/beans/Introspector/6380849/TestBeanInfo.java +++ b/test/java/beans/Introspector/6380849/TestBeanInfo.java @@ -41,8 +41,6 @@ import java.beans.Introspector; import java.lang.ref.Reference; import java.lang.reflect.Field; -import sun.awt.SunToolkit; - public class TestBeanInfo implements Runnable { private static final String[] SEARCH_PATH = { "infos" }; // NON-NLS: package name @@ -81,9 +79,6 @@ public class TestBeanInfo implements Runnable { private boolean passed; public void run() { - if (this.passed) { - SunToolkit.createNewAppContext(); - } Introspector.flushCaches(); test(FirstBean.class, FirstBeanBeanInfo.class); @@ -98,7 +93,5 @@ public class TestBeanInfo implements Runnable { test(SecondBean.class, SecondBeanBeanInfo.class); test(ThirdBean.class, null); test(ThirdBeanBeanInfo.class, ThirdBeanBeanInfo.class); - - this.passed = true; } } diff --git a/test/java/beans/Introspector/7064279/Test7064279.java b/test/java/beans/Introspector/7064279/Test7064279.java new file mode 100644 index 000000000..91d10fe7d --- /dev/null +++ b/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/7064279/test.jar b/test/java/beans/Introspector/7064279/test.jar new file mode 100644 index 0000000000000000000000000000000000000000..7516a335f766541beeef3cf70ec4772be684cc14 GIT binary patch literal 1573 zcmWIWW@Zs#-~hrq^BBz;kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0*3 zB#%f-X=JcWf2R2(O-=l%c(~Z~CC|jPE1s#o&iqvLv4|1ukUzb*tKEUtfH1-#2hn`N z50ouQEiTay0TO!2If=!^x%~&d4m$|6{lB@SGjj3Tt+rJw7Az6vnJ22>w0u>&*!$VL zT6A-{PF{PyY5|8_4THSb9A2-ZRWoP)eLlOs%+BuqJ;pf;wI3$yH?@~_{1uROvbY|) zw8%GufBCDxrN*I8UY?k??8JG_gRPo9dsQkt|Id36uPY~!%jdf=fBI*i>B&>iMoiqf z&D`E^?vnrWr?06qyqh9iDE%a3qmKHoTNc+d)@WD1+99F1Ey+Y+Lsq13Z1;tz*>_Td zZ8yI7SZWvVEx`Bu)ZIJgHlgqN0`NJUMF8Bt@NX`@c^tdr;Q> zZe7BGMfVEs&6X^xDXwjmK2u^X@o`W2j=4T|3totXEi$^rckN?y?}SxZEJuQrz8iGU ziO#Ev>p%3bo#*u$mpwX@Wg`1*B2(uJpV@R)&tG1A>H1}xmweb-G*N8E^3yEcDs>*8 z4hTe?+Ph&z$yvw$y$@x=%-@~h*?)Y2b%@Ww4^a{6M}nVoJUb9#dyS1fwmWSBf8&Z^ zQ}cpqzT;;PI+-Va+;Q*GMD4s8N0wDjT68EVBqp+Np0xIsL%LfgIkTiO2fhw0JD+XC z+nDrjxz@Sh#C0o$i?@6fSJK;jO+d@wO{sVC+nJv)%d=YkI&v}cUWpNt;zw0o@tfOt zQa)C->le;D{<^5#KV$p#mIt=>g`LjYtbvye+BD587OqyAugf7{?=?MhXR}#j=DcaI zmnb~eY?PR7vD{|cX^Z1&v-3A8EiB>Uz%5q`DaZCxFYA4+xI+Hfx$KxN)U#6DQ;Az?Mt9qc=Rw)R z4<~Qydk}GS#vQ-qlglsPR}0SR{-KnwY`Iucd#%jsJNoNlTfSf3&3Wz4&rCz%Ws_{2 z92cVBIi^g6{7gW1kl-;rQip>|^r