Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8207331

java.beans.Introspector::getBeanInfo(Class<?>) is causing class to leak

XMLWordPrintable

    • 9
    • 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


            serb Sergey Bylokhov
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: