IllegalAccessError in GeneratedMethodAccessor during reflection inflation with custom ClassLoader

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Software:
          System Software Overview:
            System Version: Ubuntu 22.04.4 LTS
          
      A DESCRIPTION OF THE PROBLEM :
      A java.lang.IllegalAccessError occurs in HotSpot during reflection inflation (when switching from NativeMethodAccessorImpl to GeneratedMethodAccessor) when the target class is loaded by a custom ClassLoader.

      The error appears after the reflection call is executed multiple times (exceeding the inflation threshold, typically 15). The JVM attempts to define a generated accessor class (sun.reflect.GeneratedMethodAccessor) to optimize the call. However, this generated class fails with an IllegalAccessError, likely because the internal reflection mechanism cannot properly access or link against the necessary classes when the call originates from a class loaded by a non-delegating custom ClassLoader.

      OpenJ9 does not exhibit this behavior and handles the reflection inflation correctly.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Compile the provided Test.java.
      2. Run with java -Xcomp Test.

      ---------- BEGIN SOURCE ----------
      import java.io.ByteArrayOutputStream;
      import java.io.InputStream;
      import java.lang.reflect.Method;

      public class Test {
          
          public static void targetMethod(float f, int i) {}
          
          public static void loopMethod(int param) {
              int i = -13;
              while ((++i) < 302) {
                  try {
                      Method m = Test.class.getMethod("targetMethod", float.class, int.class);
                      m.invoke(null, 0, param);
                  } catch (Throwable e) {
                      throw new RuntimeException(e);
                  }
              }
          }
          
          public static void main(String[] args) {
              try {
                  ClassLoader cl = new CustomLoader();
                  Class<?> cls = cl.loadClass("Test");
                  Method m = cls.getMethod("loopMethod", int.class);
                  m.invoke(null, 1);
              } catch (Exception ex) {
                  ex.printStackTrace();
              }
          }
          
          static class CustomLoader extends ClassLoader {
              public Class loadClass(String name) throws ClassNotFoundException {
                  if (!name.startsWith("java.") && name.startsWith("")) {
                      try {
                          String path = "/" + name.replace('.', '/') + ".class";
                          InputStream is = getClass().getResourceAsStream(path);
                          if (is == null) return super.loadClass(name);
                          
                          ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                          byte[] data = new byte[1024];
                          int nRead;
                          while ((nRead = is.read(data, 0, data.length)) != -1) {
                              buffer.write(data, 0, nRead);
                          }
                          
                          byte[] b = buffer.toByteArray();
                          return defineClass(name, b, 0, b.length);
                      } catch (Exception e) {
                          throw new ClassNotFoundException(name, e);
                      }
                  }
                  return super.loadClass(name);
              }
          }
      }
      ---------- END SOURCE ----------

      FREQUENCY :
      ALWAYS

            Assignee:
            Unassigned
            Reporter:
            Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: