-
Bug
-
Resolution: Duplicate
-
P4
-
9, 10, 11, 12
-
x86
-
linux
A DESCRIPTION OF THE PROBLEM :
java.beans.Introspector is using a new cache mechanism since Java9 in com.sun.beans.introspect.ClassInfo::CACHE which uses a SoftReference cache over the class, which makes the class to leak, unless clearing the cache manually or the reference.
Projects such as tomcat or classloader-leak-prevention will be required to clear that cache manually for them to work under Java11
I think the fix is:
1. Make java.beans.Introspector::flushCaches() also clear the cache in com.sun.beans.introspect.ClassInfo::CACHE. The flushCaches() is a function which called already by current Java WebApplication projects, and is expected to clear any caches of the Introspector
2. And, consider making a WeakReference to the class in ClassInfo code, but I'm not 100% sure what will be the implications of that. But as far as I understand as long as its classloader is loaded, the class will remain loaded, thus its weak reference will remain valid.
REGRESSION : Last worked in version 8u172
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Any classloader which loads classes, and then call java.beans.Introspector.getBeanInfo() will leak its classes forever, even if called java.beans.Introspector.flushCaches(); which should flush all caches completely.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After java.beans.Introspector.flushCaches() all Introspector caches will be purged, but its not, which will cause the class, and its classloader to be freed and unloaded successfully
ACTUAL -
The new cache in com.sun.beans.introspect.ClassInfo::CACHE isn't cleared since it is using a SoftReference to the class.
---------- BEGIN SOURCE ----------
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLClassLoader;
public class TestLeak {
public static void main(String[] args) throws Exception {
WeakReference<ClassLoader> loader = getLoader();
// Doesn't clear the cache in com.sun.beans.introspect.ClassInfo::CACHE
java.beans.Introspector.flushCaches();
while (loader.get() != null) {
System.gc();
Thread.sleep(1000);
System.out.println("Not freed :(");
}
System.out.println("No leak!");
}
public static void test() {
try {
// Leaks the class loaded under the classloader
java.beans.Introspector.getBeanInfo(TestLeak.class);
// The class is still leaked
java.beans.Introspector.flushFromCaches(TestLeak.class);
} catch (Exception e) {
e.printStackTrace();
}
}
private static WeakReference<ClassLoader> getLoader() throws Exception {
URL url = TestLeak.class.getProtectionDomain().getCodeSource().getLocation();
URLClassLoader loader = new URLClassLoader(new URL[] {url}, null);
Class<?> workerClass = Class.forName("TestLeak", true, loader);
workerClass.getDeclaredMethod("test").invoke(null);
loader.close();
return new WeakReference<>(loader);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Clear the cache in "com.sun.beans.introspect.ClassInfo" manually using reflection, which is prune to break.
FREQUENCY : always
java.beans.Introspector is using a new cache mechanism since Java9 in com.sun.beans.introspect.ClassInfo::CACHE which uses a SoftReference cache over the class, which makes the class to leak, unless clearing the cache manually or the reference.
Projects such as tomcat or classloader-leak-prevention will be required to clear that cache manually for them to work under Java11
I think the fix is:
1. Make java.beans.Introspector::flushCaches() also clear the cache in com.sun.beans.introspect.ClassInfo::CACHE. The flushCaches() is a function which called already by current Java WebApplication projects, and is expected to clear any caches of the Introspector
2. And, consider making a WeakReference to the class in ClassInfo code, but I'm not 100% sure what will be the implications of that. But as far as I understand as long as its classloader is loaded, the class will remain loaded, thus its weak reference will remain valid.
REGRESSION : Last worked in version 8u172
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Any classloader which loads classes, and then call java.beans.Introspector.getBeanInfo() will leak its classes forever, even if called java.beans.Introspector.flushCaches(); which should flush all caches completely.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After java.beans.Introspector.flushCaches() all Introspector caches will be purged, but its not, which will cause the class, and its classloader to be freed and unloaded successfully
ACTUAL -
The new cache in com.sun.beans.introspect.ClassInfo::CACHE isn't cleared since it is using a SoftReference to the class.
---------- BEGIN SOURCE ----------
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLClassLoader;
public class TestLeak {
public static void main(String[] args) throws Exception {
WeakReference<ClassLoader> loader = getLoader();
// Doesn't clear the cache in com.sun.beans.introspect.ClassInfo::CACHE
java.beans.Introspector.flushCaches();
while (loader.get() != null) {
System.gc();
Thread.sleep(1000);
System.out.println("Not freed :(");
}
System.out.println("No leak!");
}
public static void test() {
try {
// Leaks the class loaded under the classloader
java.beans.Introspector.getBeanInfo(TestLeak.class);
// The class is still leaked
java.beans.Introspector.flushFromCaches(TestLeak.class);
} catch (Exception e) {
e.printStackTrace();
}
}
private static WeakReference<ClassLoader> getLoader() throws Exception {
URL url = TestLeak.class.getProtectionDomain().getCodeSource().getLocation();
URLClassLoader loader = new URLClassLoader(new URL[] {url}, null);
Class<?> workerClass = Class.forName("TestLeak", true, loader);
workerClass.getDeclaredMethod("test").invoke(null);
loader.close();
return new WeakReference<>(loader);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Clear the cache in "com.sun.beans.introspect.ClassInfo" manually using reflection, which is prune to break.
FREQUENCY : always
- duplicates
-
JDK-8231454 File lock in Windows on a loaded jar due to a leak in Introspector::getBeanInfo
- Resolved
- relates to
-
JDK-8231454 File lock in Windows on a loaded jar due to a leak in Introspector::getBeanInfo
- Resolved