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

FlightRecorderMXBeanImpl is leaking the first classloader which calls it

    Details

    • Subcomponent:
      jfr
    • Introduced In Build:
      b122
    • Introduced In Version:
      9
    • Resolved In Build:
      b24
    • CPU:
      generic
    • OS:
      generic

      Backports

        Description

        A DESCRIPTION OF THE PROBLEM :
        Iterating over all the mbeans in java is causing to the leak the caller classloader.
        This used to work under Java8, but since Java9 is leaking (tested 9,10,11ea)

        When using tools such as Eclipse Memory Analyzer I see the reason is FlightRecorderMXBeanImpl mbean.

        This happens because it is calling ManagementSupport::getEventTypes()
        which calls JDKEvents::initialize()
        which calls FlightRecorder::addPeriodicEvent()
        which causes a leak since the AccessControlContext is take a reference to the caller classloader.

        The only way to mitigate is to make stuff like tomcat or classloader-leak-prevention to preload the mbean of flightrecorder to make sure it won't leak.

        this happens whether flightrecorder is turned on or off.

        REGRESSION : Last worked in version 8u172

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        see the example test case,
        basically if query all the mbeans attributes, or just the flightrecorder one, it will leak the caller classloader

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        not leaking the classloader which just wanted to query all the mbean values.
        ACTUAL -
        leaking the classloader.

        ---------- BEGIN SOURCE ----------
        import javax.management.*;
        import java.lang.management.ManagementFactory;
        import java.lang.ref.WeakReference;
        import java.net.URL;
        import java.net.URLClassLoader;

        public class TestLeak {
          public static void main(String[] args) throws Exception {
            // uncomment this line to mitigate:
            //preinitializeFlightRecorderMXBeanImpl();

            WeakReference<ClassLoader> loader = getLoader();

            while (loader.get() != null) {
              System.gc();
              Thread.sleep(1000);
              System.out.println("Not freed :(");
            }

            System.out.println("No leak!");
          }

          private static void preinitializeFlightRecorderMXBeanImpl() {
            try {
              MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
              ObjectName beanName = ObjectName.getInstance("jdk.management.jfr:type=FlightRecorder");
              MBeanInfo mBeanInfo = platformMBeanServer.getMBeanInfo(beanName);
              for (MBeanAttributeInfo att : mBeanInfo.getAttributes()) {
                platformMBeanServer.getAttribute(beanName, att.getName());
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
          }

          public static void test() {
            try {
              MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();

              for (ObjectName objectName : platformMBeanServer.queryNames(null, null)) {
                final MBeanInfo mBeanInfo = platformMBeanServer.getMBeanInfo(objectName);

                for (MBeanAttributeInfo att : mBeanInfo.getAttributes()) {
                  try {
                    final Object attValue = platformMBeanServer.getAttribute(objectName, att.getName());
                  } catch (RuntimeMBeanException e) {
                    // ignore
                  } catch (Exception e) {
                    e.printStackTrace();
                  }
                }
              }

            } 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 :
        do the function of preinitializeFlightRecorderMXBeanImpl() in your code before everything to workaround this leak

        FREQUENCY : always


          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  egahlin Erik Gahlin
                  Reporter:
                  webbuggrp Webbug Group
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  7 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: