summaryrefslogtreecommitdiff
path: root/java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch
diff options
context:
space:
mode:
Diffstat (limited to 'java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch')
-rw-r--r--java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch429
1 files changed, 429 insertions, 0 deletions
diff --git a/java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch b/java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch
new file mode 100644
index 000000000000..01dd35c1bee2
--- /dev/null
+++ b/java/openjdk6/files/icedtea/openjdk/5102804-memory_leak.patch
@@ -0,0 +1,429 @@
+# HG changeset patch
+# User andrew
+# Date 1365704033 -3600
+# Node ID 06255d9f82761abc74c30f31fda00968ffef4bc3
+# Parent a939f541de9af5ccb78225c27cd46cd7dc6bcf87
+5102804: Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param (includes WeakCache from 6397609)
+Reviewed-by: peterz
+
+diff --git a/src/share/classes/com/sun/beans/WeakCache.java b/src/share/classes/com/sun/beans/WeakCache.java
+new file mode 100644
+--- /dev/null
++++ jdk/src/share/classes/com/sun/beans/WeakCache.java
+@@ -0,0 +1,91 @@
++/*
++ * Copyright 2008 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. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun 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 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.
++ */
++package com.sun.beans;
++
++import java.lang.ref.Reference;
++import java.lang.ref.WeakReference;
++
++import java.util.Map;
++import java.util.WeakHashMap;
++
++/**
++ * A hashtable-based cache with weak keys and weak values.
++ * An entry in the map will be automatically removed
++ * when its key is no longer in the ordinary use.
++ * A value will be automatically removed as well
++ * when it is no longer in the ordinary use.
++ *
++ * @since 1.7
++ *
++ * @author Sergey A. Malenkov
++ */
++public final class WeakCache<K, V> {
++ private final Map<K, Reference<V>> map = new WeakHashMap<K, Reference<V>>();
++
++ /**
++ * Returns a value to which the specified {@code key} is mapped,
++ * or {@code null} if this map contains no mapping for the {@code key}.
++ *
++ * @param key the key whose associated value is returned
++ * @return a value to which the specified {@code key} is mapped
++ */
++ public V get(K key) {
++ Reference<V> reference = this.map.get(key);
++ if (reference == null) {
++ return null;
++ }
++ V value = reference.get();
++ if (value == null) {
++ this.map.remove(key);
++ }
++ return value;
++ }
++
++ /**
++ * Associates the specified {@code value} with the specified {@code key}.
++ * Removes the mapping for the specified {@code key} from this cache
++ * if it is present and the specified {@code value} is {@code null}.
++ * If the cache previously contained a mapping for the {@code key},
++ * the old value is replaced by the specified {@code value}.
++ *
++ * @param key the key with which the specified value is associated
++ * @param value the value to be associated with the specified key
++ */
++ public void put(K key, V value) {
++ if (value != null) {
++ this.map.put(key, new WeakReference<V>(value));
++ }
++ else {
++ 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
+--- jdk/src/share/classes/java/beans/Introspector.java
++++ jdk/src/share/classes/java/beans/Introspector.java
+@@ -25,25 +25,18 @@
+
+ package java.beans;
+
++import com.sun.beans.WeakCache;
+ 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;
+ import sun.reflect.misc.ReflectUtil;
+@@ -110,8 +103,8 @@
+ 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<Class<?>, Method[]> declaredMethodCache =
++ new WeakCache<Class<?>, Method[]>();
+
+ private static final Object BEANINFO_CACHE = new Object();
+
+@@ -177,20 +170,21 @@
+ if (!ReflectUtil.isPackageAccessible(beanClass)) {
+ return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
+ }
+- Map<Class<?>, BeanInfo> map;
+ synchronized (BEANINFO_CACHE) {
+- map = (Map<Class<?>, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE);
+- if (map == null) {
+- map = Collections.synchronizedMap(new WeakHashMap<Class<?>, BeanInfo>());
+- AppContext.getAppContext().put(BEANINFO_CACHE, map);
++ WeakCache<Class<?>, BeanInfo> beanInfoCache =
++ (WeakCache<Class<?>, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE);
++
++ if (beanInfoCache == null) {
++ beanInfoCache = new WeakCache<Class<?>, 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;
+ }
+
+ /**
+@@ -337,11 +331,13 @@
+ */
+
+ 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();
+ }
+
+ /**
+@@ -363,11 +359,13 @@
+ 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);
+ }
+
+ //======================================================================
+@@ -1306,41 +1304,26 @@
+ /*
+ * 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;
++ 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;
++ }
++ }
++ declaredMethodCache.put(clz, result);
+ }
++ return result;
+ }
+-
+- // We have to raise privilege for getDeclaredMethods
+- result = (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
+- public Object run() {
+- return fclz.getDeclaredMethods();
+- }
+- });
+-
+-
+- // 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;
+- }
+- }
+- // Add it to the cache.
+- declaredMethodCache.put(fclz, new SoftReference(result));
+- return result;
+ }
+
+ //======================================================================
+diff --git a/test/java/beans/Introspector/Test5102804.java b/test/java/beans/Introspector/Test5102804.java
+new file mode 100644
+--- /dev/null
++++ jdk/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<Class>(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;
++ }
++ }
++ }
++}