-
Bug
-
Resolution: Fixed
-
P3
-
5.0u23-rev, 6
-
b78
-
x86
-
linux_redhat_9.0, windows
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-2190775 | 6u21 | Sunita Koppar | P3 | Resolved | Fixed | b02 |
JDK-2191886 | 6u20-rev | Sunita Koppar | P3 | Closed | Fixed | b03 |
JDK-2190318 | 6u19-rev | Sunita Koppar | P3 | Resolved | Fixed | b05 |
JDK-2185536 | 6u18-rev | Sunita Koppar | P3 | Closed | Fixed | b09 |
JDK-2192897 | 5.0u25 | Sunita Koppar | P2 | Closed | Fixed | b01 |
JDK-2190172 | 5.0u24-rev | Sunita Koppar | P2 | Resolved | Fixed | b04 |
JDK-2185537 | 5.0u23-rev | Sunita Koppar | P2 | Closed | Fixed | b05 |
---%<---
package testintrospectorleak;
import java.beans.BeanInfo;
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;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
ClassLoader l = new L();
Class c = Class.forName("testintrospectorleak.Main$B", true, l);
if (c.getClassLoader() != l) throw new Error("Wrong loader for B!");
l = null; // release ref
BeanInfo bi = Introspector.getBeanInfo(c);
System.out.println("Got BeanInfo " + bi + " for " + c + " with defaultPropertyIndex " + bi.getDefaultPropertyIndex());
bi = null; // release ref
Reference r = new WeakReference(c);
c = null; // release ref
System.out.println("Class collected? " + collectible(r));
Introspector.flushCaches();
System.out.println("Class collected after I.fC? " + collectible(r));
}
// Try as hard as possible to collect a reference, even if soft.
// Copied from org.netbeans.junit.NbTestCase.assertGC.
private static boolean collectible(Reference ref) {
List alloc = new ArrayList();
int size = 100000;
for (int i = 0; i < 50; i++) {
if (ref.get() == null) {
return true;
}
System.gc();
System.runFinalization();
try {
alloc.add(new byte[size]);
size = (int) (((double) size) * 1.3);
} catch (OutOfMemoryError error) {
size = size / 2;
}
try {
if (i % 3 == 0) {
Thread.sleep(321);
}
} catch (InterruptedException t) {}
}
return false;
}
// Class loader which specifically loads B by itself (does not delegate).
// Could also load it from a different code source, but this is easier to set up.
private static final class L extends URLClassLoader {
public L() {
super(new URL[] {Main.class.getProtectionDomain().getCodeSource().getLocation()});
}
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = findLoadedClass(name);
if (c == null) {
if (!name.equals("testintrospectorleak.Main$B") && !name.equals("testintrospectorleak.Main$BBeanInfo")) {
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException e) {
c = findClass(name);
}
} else {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
// A simple bean. Loaded from L, not main app class loader.
public static final class B {
public B() {}
public int getX() {return 0;}
public void setX(int x) {}
}
// Its BeanInfo. Again loaded only from L.
public static final class BBeanInfo extends SimpleBeanInfo {
public BBeanInfo() {}
public int getDefaultPropertyIndex() {
return 0;
}
public PropertyDescriptor[] getPropertyDescriptors() {
try {
return new PropertyDescriptor[] {
new PropertyDescriptor("x", Class.forName("testintrospectorleak.Main$B")),
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
}
---%<---
Under either JDK 1.4.2_04-b02 or 1.5.0-rc-b63 on Linux, the results are similar:
---%<---
Got BeanInfo java.beans.GenericBeanInfo@757aef for class testintrospectorleak.Main$B with defaultPropertyIndex 0
Class collected? false
Class collected after I.fC? true
---%<---
If you comment out BBeanInfo, recompile (taking care to delete the old Main$BBeanInfo.class!), and run it again, then it works as desired on JDK 1.5:
---%<---
Got BeanInfo java.beans.GenericBeanInfo@757aef for class testintrospectorleak.Main$B with defaultPropertyIndex -1
Class collected? true
Class collected after I.fC? true
---%<---
though not on JDK 1.4.2:
---%<---
Got BeanInfo java.beans.GenericBeanInfo@1be2d65 for class testintrospectorleak.Main$B with defaultPropertyIndex -1
Class collected? false
Class collected after I.fC? true
---%<---
Meaning that the basic case - no custom BeanInfo - was fixed in JDK 1.5, but not the case where there is custom BeanInfo.
Interpretation of results: Introspector's cache (beanInfoCache) uses strong refs as values, meaning that GenericBeanInfo instances are held strongly. With no custom BeanInfo, it seems that all of the GBI's fields refer at most softly to the original Class. However if there is a custom BeanInfo, GBI seems to hold this strongly in the targetBeanInfo field. In that case, the fact that bIC is a WeakHashMap is irrelevant; the key cannot be collected since the value holds it strongly anyway.
Why is this relevant? All sorts of code can call Introspector.getBeanInfo for a variety of reasons, it is pretty common. If some class is loaded from a temporary class loader (e.g. an Ant <taskdef> with nested <classpath>, but many other examples are possible), and it has a specific BeanInfo loaded by I.gBI, then the class will not be collectible. This means the ClassLoader is not collectible either, nor any of the other classes loaded by it, nor their static fields, nor any parent class loaders, etc. This can cause a huge memory leak in some cases. (And on Windows could result in a JAR file lock being held open for the life of the VM.) In particular, it is thought to be the root cause of the NetBeans IDE memory leak bug described at
http://www.netbeans.org/issues/show_bug.cgi?id=46532
Petr Nejedly claims that the bug is reproducible in the stock JDK using an unpatched NetBeans but that if Introspector's cache is patched to not refer strongly to values, it is not reproducible.
###@###.### 2004-09-15
- backported by
-
JDK-2190172 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Resolved
-
JDK-2190318 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Resolved
-
JDK-2190775 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Resolved
-
JDK-2185537 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Closed
-
JDK-2192897 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Closed
-
JDK-2185536 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Closed
-
JDK-2191886 Memory leak in Introspector.getBeanInfo(Class) for custom BeanInfo: Class param
- Closed
- relates to
-
JDK-6905574 DOC: Introspector's javadoc contains wrong statement
- Resolved
-
JDK-7195106 REGRESSION : There is no way to get Icon inf, once Softreference is released
- Closed
-
JDK-6389107 (ref) Request Reference.link(Object,Object) for "biweak" caches
- Open
-
JDK-6963811 Deadlock-prone locking changes in Introspector
- Resolved
-
JDK-6930871 CR 5102804 bugfix test promoted for 5.0_23-rev_b05 jfb produced a new issue
- Resolved
-
JDK-6905515 Test failed: java/beans/XMLEncoder/6329581/Test6329581.java
- Resolved
-
JDK-6921057 REGRESSION: persistence delegate issue on Windows and Linux against 5.u23b03/6u17b11
- Closed
-
JDK-2187827 REGRESSION: persistence delegate issue on Windows and Linux against 5.u23b03/6u17b11
- Resolved
-
JDK-2187826 REGRESSION: persistence delegate issue on Windows and Linux against 5.u23b03/6u17b11
- Closed