diff --git a/src/share/classes/com/sun/beans/WeakCache.java b/src/share/classes/com/sun/beans/WeakCache.java index 461c48e1fd3c7a55ae104262ee13f14c2c0b2a06..3aa6373dc46587f926a102bb0accf59683ab612a 100644 --- a/src/share/classes/com/sun/beans/WeakCache.java +++ b/src/share/classes/com/sun/beans/WeakCache.java @@ -81,4 +81,11 @@ public final class WeakCache { this.map.remove(key); } } + + /** + * Removes all of the mappings from this cache. + */ + public void clear() { + this.map.clear(); + } } diff --git a/src/share/classes/java/beans/Introspector.java b/src/share/classes/java/beans/Introspector.java index 3d56599576d9f92119df53301416afe89b7ce958..f0ce2a85b816bfc6faf8bcc9d26a17e09bd69f91 100644 --- a/src/share/classes/java/beans/Introspector.java +++ b/src/share/classes/java/beans/Introspector.java @@ -25,26 +25,19 @@ package java.beans; +import com.sun.beans.WeakCache; import com.sun.beans.finder.BeanInfoFinder; import com.sun.beans.finder.ClassFinder; -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import java.util.Collections; import java.util.Map; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.EventListener; import java.util.List; -import java.util.WeakHashMap; import java.util.TreeMap; import sun.awt.AppContext; @@ -112,8 +105,8 @@ public class Introspector { public final static int IGNORE_ALL_BEANINFO = 3; // Static Caches to speed up introspection. - private static Map declaredMethodCache = - Collections.synchronizedMap(new WeakHashMap()); + private static WeakCache, Method[]> declaredMethodCache = + new WeakCache, Method[]>(); private static final Object BEANINFO_CACHE = new Object(); @@ -174,20 +167,21 @@ public class Introspector { if (!ReflectUtil.isPackageAccessible(beanClass)) { return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); } - Map, BeanInfo> map; synchronized (BEANINFO_CACHE) { - map = (Map, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); - if (map == null) { - map = Collections.synchronizedMap(new WeakHashMap, BeanInfo>()); - AppContext.getAppContext().put(BEANINFO_CACHE, map); + WeakCache, BeanInfo> beanInfoCache = + (WeakCache, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); + + if (beanInfoCache == null) { + beanInfoCache = new WeakCache, BeanInfo>(); + AppContext.getAppContext().put(BEANINFO_CACHE, beanInfoCache); } + BeanInfo beanInfo = beanInfoCache.get(beanClass); + if (beanInfo == null) { + beanInfo = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); + beanInfoCache.put(beanClass, beanInfo); + } + return beanInfo; } - BeanInfo bi = map.get(beanClass); - if (bi == null) { - bi = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); - map.put(beanClass, bi); - } - return bi; } /** @@ -359,11 +353,13 @@ public class Introspector { */ public static void flushCaches() { - Map map = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (map != null) { - map.clear(); + synchronized (BEANINFO_CACHE) { + WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE); + if (beanInfoCache != null) { + beanInfoCache.clear(); + } + declaredMethodCache.clear(); } - declaredMethodCache.clear(); } /** @@ -385,11 +381,13 @@ public class Introspector { if (clz == null) { throw new NullPointerException(); } - Map map = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (map != null) { - map.remove(clz); + synchronized (BEANINFO_CACHE) { + WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE); + if (beanInfoCache != null) { + beanInfoCache.put(clz, null); + } + declaredMethodCache.put(clz, null); } - declaredMethodCache.remove(clz); } //====================================================================== @@ -1272,41 +1270,26 @@ public class Introspector { /* * Internal method to return *public* methods within a class. */ - private static synchronized Method[] getPublicDeclaredMethods(Class clz) { + private static Method[] getPublicDeclaredMethods(Class clz) { // Looking up Class.getDeclaredMethods is relatively expensive, // so we cache the results. - Method[] result = null; if (!ReflectUtil.isPackageAccessible(clz)) { return new Method[0]; } - final Class fclz = clz; - Reference ref = (Reference)declaredMethodCache.get(fclz); - if (ref != null) { - result = (Method[])ref.get(); - if (result != null) { - return result; - } - } - - // We have to raise privilege for getDeclaredMethods - result = (Method[]) AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - return fclz.getDeclaredMethods(); + synchronized (BEANINFO_CACHE) { + Method[] result = declaredMethodCache.get(clz); + if (result == null) { + result = clz.getMethods(); + for (int i = 0; i < result.length; i++) { + Method method = result[i]; + if (!method.getDeclaringClass().equals(clz)) { + result[i] = null; + } } - }); - - - // Null out any non-public methods. - for (int i = 0; i < result.length; i++) { - Method method = result[i]; - int mods = method.getModifiers(); - if (!Modifier.isPublic(mods)) { - result[i] = null; + declaredMethodCache.put(clz, result); } + return result; } - // Add it to the cache. - declaredMethodCache.put(fclz, new SoftReference(result)); - return result; } //====================================================================== diff --git a/src/windows/lib/tzmappings b/src/windows/lib/tzmappings index ec2732f64fbc6e0aa7ea0eb359f933a9dfe8a77d..05309c9aef8f3f3d067d873e18a4168a88421d51 100644 --- a/src/windows/lib/tzmappings +++ b/src/windows/lib/tzmappings @@ -92,7 +92,7 @@ Bangkok Standard Time:14,15::Asia/Bangkok: North Asia Standard Time:14,15::Asia/Krasnoyarsk: SE Asia:14,15::Asia/Bangkok: SE Asia Standard Time:14,15::Asia/Bangkok: -North Asia East Standard Time:16,17::Asia/Ulaanbaatar: +North Asia East Standard Time:16,17:RU:Asia/Irkutsk: Singapore:16,17:SG:Asia/Singapore: Singapore Standard Time:16,17:SG:Asia/Singapore: Taipei:16,17::Asia/Taipei: @@ -184,4 +184,5 @@ Venezuela Standard Time:915,915::America/Caracas: Kamchatka Standard Time:916,916:RU:Asia/Kamchatka: Paraguay Standard Time:917,917:PY:America/Asuncion: Western Brazilian Standard Time:918,918:BR:America/Rio_Branco: -Armenian Standard Time:919,919:AM:Asia/Yerevan: +Ulaanbaatar Standard Time:919,919::Asia/Ulaanbaatar: +Armenian Standard Time:920,920:AM:Asia/Yerevan: diff --git a/test/java/beans/Introspector/Test5102804.java b/test/java/beans/Introspector/Test5102804.java new file mode 100644 index 0000000000000000000000000000000000000000..71f23f9294133bde362ee45fee71b338323b0d74 --- /dev/null +++ b/test/java/beans/Introspector/Test5102804.java @@ -0,0 +1,155 @@ +/* + * Copyright 2009 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 5102804 + * @summary Tests memory leak + * @author Sergey Malenkov + */ + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; + +public class Test5102804 { + private static final String BEAN_NAME = "Test5102804$Example"; + private static final String BEAN_INFO_NAME = BEAN_NAME + "BeanInfo"; + + public static void main(String[] args) { + if (!isCollectible(getReference())) + throw new Error("Reference is not collected"); + } + + private static Reference getReference() { + try { + ClassLoader loader = new Loader(); + Class type = Class.forName(BEAN_NAME, true, loader); + if (!type.getClassLoader().equals(loader)) { + throw new Error("Wrong class loader"); + } + BeanInfo info = Introspector.getBeanInfo(type); + if (0 != info.getDefaultPropertyIndex()) { + throw new Error("Wrong bean info found"); + } + return new WeakReference(type); + } + catch (IntrospectionException exception) { + throw new Error("Introspection Error", exception); + } + catch (ClassNotFoundException exception) { + throw new Error("Class Not Found", exception); + } + } + + private static boolean isCollectible(Reference reference) { + int[] array = new int[10]; + while (true) { + try { + array = new int[array.length + array.length / 3]; + } + catch (OutOfMemoryError error) { + return null == reference.get(); + } + } + } + + /** + * Custom class loader to load the Example class by itself. + * Could also load it from a different code source, but this is easier to set up. + */ + private static final class Loader extends URLClassLoader { + Loader() { + super(new URL[] { + Test5102804.class.getProtectionDomain().getCodeSource().getLocation() + }); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class c = findLoadedClass(name); + if (c == null) { + if (BEAN_NAME.equals(name) || BEAN_INFO_NAME.equals(name)) { + c = findClass(name); + } + else try { + c = getParent().loadClass(name); + } + catch (ClassNotFoundException exception) { + c = findClass(name); + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } + + /** + * A simple bean to load from the Loader class, not main class loader. + */ + public static final class Example { + private int value; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + } + + /** + * The BeanInfo for the Example class. + * It is also loaded from the Loader class. + */ + public static final class ExampleBeanInfo extends SimpleBeanInfo { + @Override + public int getDefaultPropertyIndex() { + return 0; + } + + @Override + public PropertyDescriptor[] getPropertyDescriptors() { + try { + return new PropertyDescriptor[] { + new PropertyDescriptor("value", Class.forName(BEAN_NAME)) + }; + } + catch (ClassNotFoundException exception) { + return null; + } + catch (IntrospectionException exception) { + return null; + } + } + } +}