注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

和申的个人主页

专注于java开发,1985wanggang

 
 
 

日志

 
 

关于反射调用方法的一个log [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__]  

2011-09-29 11:18:46|  分类: java调优 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
刚才在JavaEye问答频道看到了这么一个问题
liu0013 写道
[Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__]
请问报这个是什么意思?


这是Sun实现的Java标准库的一个细节。下面举例稍微讲解一下。
假如有这么一个类A:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. public class A {   
  2.     public void foo(String name) {   
  3.         System.out.println("Hello, " + name);   
  4.     }   
  5. }  
public class A {      public void foo(String name) {          System.out.println("Hello, " + name);      }  }

可以编写另外一个类来反射调用A上的方法:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. import java.lang.reflect.Method;   
  2.   
  3. public class TestClassLoad {   
  4.     public static void main(String[] args) throws Exception {   
  5.         Class<?> clz = Class.forName("A");   
  6.         Object o = clz.newInstance();   
  7.         Method m = clz.getMethod("foo", String.class);   
  8.         for (int i = 0; i < 16; i++) {   
  9.             m.invoke(o, Integer.toString(i));   
  10.         }   
  11.     }   
  12. }  
import java.lang.reflect.Method;    public class TestClassLoad {      public static void main(String[] args) throws Exception {          Class<?> clz = Class.forName("A");          Object o = clz.newInstance();          Method m = clz.getMethod("foo", String.class);          for (int i = 0; i < 16; i++) {              m.invoke(o, Integer.toString(i));          }      }  }

注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。

回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:
Command prompt代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. java -XX:+TraceClassLoading TestClassLoad  
java -XX:+TraceClassLoading TestClassLoad

可以看到输出了一大堆log,把其中相关的部分截取出来如下:(完整的log可以从附件下载)
Log代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. [Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/]   
  2. [Loaded A from file:/D:/temp_code/test_java_classload/]   
  3. [Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file]   
  4. [Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file]   
  5. Hello, 0  
  6. Hello, 1  
  7. Hello, 2  
  8. Hello, 3  
  9. Hello, 4  
  10. Hello, 5  
  11. Hello, 6  
  12. Hello, 7  
  13. Hello, 8  
  14. Hello, 9  
  15. Hello, 10  
  16. Hello, 11  
  17. Hello, 12  
  18. Hello, 13  
  19. Hello, 14  
  20. [Loaded sun.reflect.ClassFileConstants from shared objects file]   
  21. [Loaded sun.reflect.AccessorGenerator from shared objects file]   
  22. [Loaded sun.reflect.MethodAccessorGenerator from shared objects file]   
  23. [Loaded sun.reflect.ByteVectorFactory from shared objects file]   
  24. [Loaded sun.reflect.ByteVector from shared objects file]   
  25. [Loaded sun.reflect.ByteVectorImpl from shared objects file]   
  26. [Loaded sun.reflect.ClassFileAssembler from shared objects file]   
  27. [Loaded sun.reflect.UTF8 from shared objects file]   
  28. [Loaded java.lang.Void from shared objects file]   
  29. [Loaded sun.reflect.Label from shared objects file]   
  30. [Loaded sun.reflect.Label$PatchInfo from shared objects file]   
  31. [Loaded java.util.AbstractList$Itr from shared objects file]   
  32. [Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file]   
  33. [Loaded sun.reflect.ClassDefiner from shared objects file]   
  34. [Loaded sun.reflect.ClassDefiner$1 from shared objects file]   
  35. [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]   
  36. Hello, 15  
[Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/]  [Loaded A from file:/D:/temp_code/test_java_classload/]  [Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file]  [Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file]  Hello, 0  Hello, 1  Hello, 2  Hello, 3  Hello, 4  Hello, 5  Hello, 6  Hello, 7  Hello, 8  Hello, 9  Hello, 10  Hello, 11  Hello, 12  Hello, 13  Hello, 14  [Loaded sun.reflect.ClassFileConstants from shared objects file]  [Loaded sun.reflect.AccessorGenerator from shared objects file]  [Loaded sun.reflect.MethodAccessorGenerator from shared objects file]  [Loaded sun.reflect.ByteVectorFactory from shared objects file]  [Loaded sun.reflect.ByteVector from shared objects file]  [Loaded sun.reflect.ByteVectorImpl from shared objects file]  [Loaded sun.reflect.ClassFileAssembler from shared objects file]  [Loaded sun.reflect.UTF8 from shared objects file]  [Loaded java.lang.Void from shared objects file]  [Loaded sun.reflect.Label from shared objects file]  [Loaded sun.reflect.Label$PatchInfo from shared objects file]  [Loaded java.util.AbstractList$Itr from shared objects file]  [Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file]  [Loaded sun.reflect.ClassDefiner from shared objects file]  [Loaded sun.reflect.ClassDefiner$1 from shared objects file]  [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]  Hello, 15

可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]这么一行。这是哪里来的呢?

先来看看JDK里Method.invoke()是怎么实现的。
java.lang.reflect.Method:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. public final  
  2.     class Method extends AccessibleObject implements GenericDeclaration,    
  3.                              Member {   
  4.     // ...   
  5.        
  6.     private volatile MethodAccessor methodAccessor;   
  7.     // For sharing of MethodAccessors. This branching structure is   
  8.     // currently only two levels deep (i.e., one root Method and   
  9.     // potentially many Method objects pointing to it.)   
  10.     private Method              root;   
  11.   
  12.     // ...   
  13.        
  14.     public Object invoke(Object obj, Object... args)   
  15.             throws IllegalAccessException, IllegalArgumentException,   
  16.             InvocationTargetException   
  17.     {   
  18.         if (!override) {   
  19.             if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {   
  20.                 Class caller = Reflection.getCallerClass(1);   
  21.                 Class targetClass = ((obj == null || !Modifier.isProtected(modifiers))   
  22.                                      ? clazz   
  23.                                      : obj.getClass());   
  24.                 boolean cached;   
  25.                 synchronized (this) {   
  26.                     cached = (securityCheckCache == caller)   
  27.                         && (securityCheckTargetClassCache == targetClass);   
  28.                 }   
  29.                 if (!cached) {   
  30.                     Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);   
  31.                     synchronized (this) {   
  32.                     securityCheckCache = caller;   
  33.                     securityCheckTargetClassCache = targetClass;   
  34.                     }   
  35.                 }   
  36.             }   
  37.         }   
  38.         if (methodAccessor == null) acquireMethodAccessor();   
  39.         return methodAccessor.invoke(obj, args);   
  40.     }   
  41.        
  42.     // NOTE that there is no synchronization used here. It is correct   
  43.     // (though not efficient) to generate more than one MethodAccessor   
  44.     // for a given Method. However, avoiding synchronization will   
  45.     // probably make the implementation more scalable.   
  46.     private void acquireMethodAccessor() {   
  47.         // First check to see if one has been created yet, and take it   
  48.         // if so   
  49.         MethodAccessor tmp = null;   
  50.         if (root != null) tmp = root.getMethodAccessor();   
  51.         if (tmp != null) {   
  52.             methodAccessor = tmp;   
  53.             return;   
  54.         }   
  55.         // Otherwise fabricate one and propagate it up to the root   
  56.         tmp = reflectionFactory.newMethodAccessor(this);   
  57.         setMethodAccessor(tmp);   
  58.     }   
  59.        
  60.     // ...   
  61. }  
public final      class Method extends AccessibleObject implements GenericDeclaration,                                Member {      // ...            private volatile MethodAccessor methodAccessor;      // For sharing of MethodAccessors. This branching structure is      // currently only two levels deep (i.e., one root Method and      // potentially many Method objects pointing to it.)      private Method              root;        // ...            public Object invoke(Object obj, Object... args)              throws IllegalAccessException, IllegalArgumentException,              InvocationTargetException      {          if (!override) {              if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                  Class caller = Reflection.getCallerClass(1);                  Class targetClass = ((obj == null || !Modifier.isProtected(modifiers))                                       ? clazz                                       : obj.getClass());                  boolean cached;                  synchronized (this) {                      cached = (securityCheckCache == caller)                          && (securityCheckTargetClassCache == targetClass);                  }                  if (!cached) {                      Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);                      synchronized (this) {                      securityCheckCache = caller;                      securityCheckTargetClassCache = targetClass;                      }                  }              }          }          if (methodAccessor == null) acquireMethodAccessor();          return methodAccessor.invoke(obj, args);      }            // NOTE that there is no synchronization used here. It is correct      // (though not efficient) to generate more than one MethodAccessor      // for a given Method. However, avoiding synchronization will      // probably make the implementation more scalable.      private void acquireMethodAccessor() {          // First check to see if one has been created yet, and take it          // if so          MethodAccessor tmp = null;          if (root != null) tmp = root.getMethodAccessor();          if (tmp != null) {              methodAccessor = tmp;              return;          }          // Otherwise fabricate one and propagate it up to the root          tmp = reflectionFactory.newMethodAccessor(this);          setMethodAccessor(tmp);      }            // ...  }

可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。
每个实际的Java方法只有一个对应的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。

那么MethodAccessor是啥呢?
sun.reflect.MethodAccessor:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. public interface MethodAccessor {   
  2.     /** Matches specification in {@link java.lang.reflect.Method} */  
  3.     public Object invoke(Object obj, Object[] args)   
  4.         throws IllegalArgumentException, InvocationTargetException;   
  5. }  
public interface MethodAccessor {      /** Matches specification in {@link java.lang.reflect.Method} */      public Object invoke(Object obj, Object[] args)          throws IllegalArgumentException, InvocationTargetException;  }

可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。

创建MethodAccessor实例的是ReflectionFactory。
sun.reflect.ReflectionFactory:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. public class ReflectionFactory {   
  2.        
  3.     private static boolean initted = false;   
  4.        
  5.     // ...   
  6.   
  7.     //   
  8.     // "Inflation" mechanism. Loading bytecodes to implement   
  9.     // Method.invoke() and Constructor.newInstance() currently costs   
  10.     // 3-4x more than an invocation via native code for the first   
  11.     // invocation (though subsequent invocations have been benchmarked   
  12.     // to be over 20x faster). Unfortunately this cost increases   
  13.     // startup time for certain applications that use reflection   
  14.     // intensively (but only once per class) to bootstrap themselves.   
  15.     // To avoid this penalty we reuse the existing JVM entry points   
  16.     // for the first few invocations of Methods and Constructors and   
  17.     // then switch to the bytecode-based implementations.   
  18.     //   
  19.     // Package-private to be accessible to NativeMethodAccessorImpl   
  20.     // and NativeConstructorAccessorImpl   
  21.     private static boolean noInflation        = false;   
  22.     private static int     inflationThreshold = 15;   
  23.        
  24.     // ...   
  25.        
  26.     /** We have to defer full initialization of this class until after  
  27.         the static initializer is run since java.lang.reflect.Method's  
  28.         static initializer (more properly, that for  
  29.         java.lang.reflect.AccessibleObject) causes this class's to be  
  30.         run, before the system properties are set up. */  
  31.     private static void checkInitted() {   
  32.         if (initted) return;   
  33.         AccessController.doPrivileged(new PrivilegedAction() {   
  34.                 public Object run() {   
  35.                     // Tests to ensure the system properties table is fully   
  36.                     // initialized. This is needed because reflection code is   
  37.                     // called very early in the initialization process (before   
  38.                     // command-line arguments have been parsed and therefore   
  39.                     // these user-settable properties installed.) We assume that   
  40.                     // if System.out is non-null then the System class has been   
  41.                     // fully initialized and that the bulk of the startup code   
  42.                     // has been run.   
  43.   
  44.                     if (System.out == null) {   
  45.                         // java.lang.System not yet fully initialized   
  46.                         return null;   
  47.                     }   
  48.   
  49.                     String val = System.getProperty("sun.reflect.noInflation");   
  50.                     if (val != null && val.equals("true")) {   
  51.                         noInflation = true;   
  52.                     }   
  53.   
  54.                     val = System.getProperty("sun.reflect.inflationThreshold");   
  55.                     if (val != null) {   
  56.                         try {   
  57.                             inflationThreshold = Integer.parseInt(val);   
  58.                         } catch (NumberFormatException e) {   
  59.                             throw (RuntimeException)    
  60.                                 new RuntimeException("Unable to parse property sun.reflect.inflationThreshold").   
  61.                                     initCause(e);   
  62.                         }   
  63.                     }   
  64.   
  65.                     initted = true;   
  66.                     return null;   
  67.                 }   
  68.             });   
  69.     }   
  70.        
  71.     // ...   
  72.        
  73.     public MethodAccessor newMethodAccessor(Method method) {   
  74.         checkInitted();   
  75.   
  76.         if (noInflation) {   
  77.             return new MethodAccessorGenerator().   
  78.                 generateMethod(method.getDeclaringClass(),   
  79.                                method.getName(),   
  80.                                method.getParameterTypes(),   
  81.                                method.getReturnType(),   
  82.                                method.getExceptionTypes(),   
  83.                                method.getModifiers());   
  84.         } else {   
  85.             NativeMethodAccessorImpl acc =   
  86.                 new NativeMethodAccessorImpl(method);   
  87.             DelegatingMethodAccessorImpl res =   
  88.                 new DelegatingMethodAccessorImpl(acc);   
  89.             acc.setParent(res);   
  90.             return res;   
  91.         }   
  92.     }   
  93. }  
public class ReflectionFactory {            private static boolean initted = false;            // ...        //      // "Inflation" mechanism. Loading bytecodes to implement      // Method.invoke() and Constructor.newInstance() currently costs      // 3-4x more than an invocation via native code for the first      // invocation (though subsequent invocations have been benchmarked      // to be over 20x faster). Unfortunately this cost increases      // startup time for certain applications that use reflection      // intensively (but only once per class) to bootstrap themselves.      // To avoid this penalty we reuse the existing JVM entry points      // for the first few invocations of Methods and Constructors and      // then switch to the bytecode-based implementations.      //      // Package-private to be accessible to NativeMethodAccessorImpl      // and NativeConstructorAccessorImpl      private static boolean noInflation        = false;      private static int     inflationThreshold = 15;            // ...            /** We have to defer full initialization of this class until after          the static initializer is run since java.lang.reflect.Method's          static initializer (more properly, that for          java.lang.reflect.AccessibleObject) causes this class's to be          run, before the system properties are set up. */      private static void checkInitted() {          if (initted) return;          AccessController.doPrivileged(new PrivilegedAction() {                  public Object run() {                      // Tests to ensure the system properties table is fully                      // initialized. This is needed because reflection code is                      // called very early in the initialization process (before                      // command-line arguments have been parsed and therefore                      // these user-settable properties installed.) We assume that                      // if System.out is non-null then the System class has been                      // fully initialized and that the bulk of the startup code                      // has been run.                        if (System.out == null) {                          // java.lang.System not yet fully initialized                          return null;                      }                        String val = System.getProperty("sun.reflect.noInflation");                      if (val != null && val.equals("true")) {                          noInflation = true;                      }                        val = System.getProperty("sun.reflect.inflationThreshold");                      if (val != null) {                          try {                              inflationThreshold = Integer.parseInt(val);                          } catch (NumberFormatException e) {                              throw (RuntimeException)                                   new RuntimeException("Unable to parse property sun.reflect.inflationThreshold").                                      initCause(e);                          }                      }                        initted = true;                      return null;                  }              });      }            // ...            public MethodAccessor newMethodAccessor(Method method) {          checkInitted();            if (noInflation) {              return new MethodAccessorGenerator().                  generateMethod(method.getDeclaringClass(),                                 method.getName(),                                 method.getParameterTypes(),                                 method.getReturnType(),                                 method.getExceptionTypes(),                                 method.getModifiers());          } else {              NativeMethodAccessorImpl acc =                  new NativeMethodAccessorImpl(method);              DelegatingMethodAccessorImpl res =                  new DelegatingMethodAccessorImpl(acc);              acc.setParent(res);              return res;          }      }  }

这里就可以看到有趣的地方了。如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
Sun的JDK是从1.4系开始采用这种优化的,主要作者是Ken Russell

上面看到了ReflectionFactory.newMethodAccessor()生产MethodAccessor的逻辑,在“开头若干次”时用到的DelegatingMethodAccessorImpl代码如下:
sun.reflect.DelegatingMethodAccessorImpl:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. /** Delegates its invocation to another MethodAccessorImpl and can  
  2.     change its delegate at run time. */  
  3.   
  4. class DelegatingMethodAccessorImpl extends MethodAccessorImpl {   
  5.     private MethodAccessorImpl delegate;   
  6.   
  7.     DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {   
  8.         setDelegate(delegate);   
  9.     }       
  10.   
  11.     public Object invoke(Object obj, Object[] args)   
  12.         throws IllegalArgumentException, InvocationTargetException   
  13.     {   
  14.         return delegate.invoke(obj, args);   
  15.     }   
  16.   
  17.     void setDelegate(MethodAccessorImpl delegate) {   
  18.         this.delegate = delegate;   
  19.     }   
  20. }  
/** Delegates its invocation to another MethodAccessorImpl and can      change its delegate at run time. */    class DelegatingMethodAccessorImpl extends MethodAccessorImpl {      private MethodAccessorImpl delegate;        DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {          setDelegate(delegate);      }            public Object invoke(Object obj, Object[] args)          throws IllegalArgumentException, InvocationTargetException      {          return delegate.invoke(obj, args);      }        void setDelegate(MethodAccessorImpl delegate) {          this.delegate = delegate;      }  }

这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。

然后下面就是native版MethodAccessor的Java一侧的声明:
sun.reflect.NativeMethodAccessorImpl:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. /** Used only for the first few invocations of a Method; afterward,  
  2.     switches to bytecode-based implementation */  
  3.   
  4. class NativeMethodAccessorImpl extends MethodAccessorImpl {   
  5.     private Method method;   
  6.     private DelegatingMethodAccessorImpl parent;   
  7.     private int numInvocations;   
  8.   
  9.     NativeMethodAccessorImpl(Method method) {   
  10.         this.method = method;   
  11.     }       
  12.   
  13.     public Object invoke(Object obj, Object[] args)   
  14.         throws IllegalArgumentException, InvocationTargetException   
  15.     {   
  16.         if (++numInvocations > ReflectionFactory.inflationThreshold()) {   
  17.             MethodAccessorImpl acc = (MethodAccessorImpl)   
  18.                 new MethodAccessorGenerator().   
  19.                     generateMethod(method.getDeclaringClass(),   
  20.                                    method.getName(),   
  21.                                    method.getParameterTypes(),   
  22.                                    method.getReturnType(),   
  23.                                    method.getExceptionTypes(),   
  24.                                    method.getModifiers());   
  25.             parent.setDelegate(acc);   
  26.         }   
  27.            
  28.         return invoke0(method, obj, args);   
  29.     }   
  30.   
  31.     void setParent(DelegatingMethodAccessorImpl parent) {   
  32.         this.parent = parent;   
  33.     }   
  34.   
  35.     private static native Object invoke0(Method m, Object obj, Object[] args);   
  36. }  
/** Used only for the first few invocations of a Method; afterward,      switches to bytecode-based implementation */    class NativeMethodAccessorImpl extends MethodAccessorImpl {      private Method method;      private DelegatingMethodAccessorImpl parent;      private int numInvocations;        NativeMethodAccessorImpl(Method method) {          this.method = method;      }            public Object invoke(Object obj, Object[] args)          throws IllegalArgumentException, InvocationTargetException      {          if (++numInvocations > ReflectionFactory.inflationThreshold()) {              MethodAccessorImpl acc = (MethodAccessorImpl)                  new MethodAccessorGenerator().                      generateMethod(method.getDeclaringClass(),                                     method.getName(),                                     method.getParameterTypes(),                                     method.getReturnType(),                                     method.getExceptionTypes(),                                     method.getModifiers());              parent.setDelegate(acc);          }                    return invoke0(method, obj, args);      }        void setParent(DelegatingMethodAccessorImpl parent) {          this.parent = parent;      }        private static native Object invoke0(Method m, Object obj, Object[] args);  }

每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。

注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:
C代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0   
  2. (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)   
  3. {   
  4.     return JVM_InvokeMethod(env, m, obj, args);   
  5. }  
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0  (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)  {      return JVM_InvokeMethod(env, m, obj, args);  }

C++代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))   
  2.   JVMWrapper("JVM_InvokeMethod");   
  3.   Handle method_handle;   
  4.   if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {   
  5.     method_handle = Handle(THREAD, JNIHandles::resolve(method));   
  6.     Handle receiver(THREAD, JNIHandles::resolve(obj));   
  7.     objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));   
  8.     oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);   
  9.     jobject res = JNIHandles::make_local(env, result);   
  10.     if (JvmtiExport::should_post_vm_object_alloc()) {   
  11.       oop ret_type = java_lang_reflect_Method::return_type(method_handle());   
  12.       assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");   
  13.       if (java_lang_Class::is_primitive(ret_type)) {   
  14.         // Only for primitive type vm allocates memory for java object.   
  15.         // See box() method.   
  16.         JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);   
  17.       }   
  18.     }   
  19.     return res;   
  20.   } else {   
  21.     THROW_0(vmSymbols::java_lang_StackOverflowError());   
  22.   }   
  23. JVM_END  
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))    JVMWrapper("JVM_InvokeMethod");    Handle method_handle;    if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {      method_handle = Handle(THREAD, JNIHandles::resolve(method));      Handle receiver(THREAD, JNIHandles::resolve(obj));      objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));      oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);      jobject res = JNIHandles::make_local(env, result);      if (JvmtiExport::should_post_vm_object_alloc()) {        oop ret_type = java_lang_reflect_Method::return_type(method_handle());        assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");        if (java_lang_Class::is_primitive(ret_type)) {          // Only for primitive type vm allocates memory for java object.          // See box() method.          JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);        }      }      return res;    } else {      THROW_0(vmSymbols::java_lang_StackOverflowError());    }  JVM_END

其中的关键又是Reflection::invoke_method():
C++代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. // This would be nicer if, say, java.lang.reflect.Method was a subclass   
  2. // of java.lang.reflect.Constructor   
  3.   
  4. oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {   
  5.   oop mirror             = java_lang_reflect_Method::clazz(method_mirror);   
  6.   int slot               = java_lang_reflect_Method::slot(method_mirror);   
  7.   bool override          = java_lang_reflect_Method::override(method_mirror) != 0;   
  8.   objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));   
  9.   
  10.   oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);   
  11.   BasicType rtype;   
  12.   if (java_lang_Class::is_primitive(return_type_mirror)) {   
  13.     rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);   
  14.   } else {   
  15.     rtype = T_OBJECT;   
  16.   }   
  17.   
  18.   instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror));   
  19.   methodOop m = klass->method_with_idnum(slot);   
  20.   if (m == NULL) {   
  21.     THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");   
  22.   }   
  23.   methodHandle method(THREAD, m);   
  24.   
  25.   return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);   
  26. }  
// This would be nicer if, say, java.lang.reflect.Method was a subclass  // of java.lang.reflect.Constructor    oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {    oop mirror             = java_lang_reflect_Method::clazz(method_mirror);    int slot               = java_lang_reflect_Method::slot(method_mirror);    bool override          = java_lang_reflect_Method::override(method_mirror) != 0;    objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));      oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);    BasicType rtype;    if (java_lang_Class::is_primitive(return_type_mirror)) {      rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);    } else {      rtype = T_OBJECT;    }      instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror));    methodOop m = klass->method_with_idnum(slot);    if (m == NULL) {      THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");    }    methodHandle method(THREAD, m);      return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);  }

再下去就深入到HotSpot VM的内部了,本文就在这里打住吧。有同学有兴趣深究的话以后可以再写一篇讨论native版的实现。

回到Java的一侧。MethodAccessorGenerator长啥样呢?由于代码太长,这里就不完整贴了,有兴趣的可以到OpenJDK 6的Mercurial仓库看:OpenJDK 6 build 17的MethodAccessorGenerator。它的基本工作就是在内存里生成新的专用Java类,并将其加载。就贴这么一个方法:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. private static synchronized String generateName(boolean isConstructor,   
  2.                                                 boolean forSerialization)   
  3. {   
  4.     if (isConstructor) {   
  5.         if (forSerialization) {   
  6.             int num = ++serializationConstructorSymnum;   
  7.             return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;   
  8.         } else {   
  9.             int num = ++constructorSymnum;   
  10.             return "sun/reflect/GeneratedConstructorAccessor" + num;   
  11.         }   
  12.     } else {   
  13.         int num = ++methodSymnum;   
  14.         return "sun/reflect/GeneratedMethodAccessor" + num;   
  15.     }   
  16. }  
private static synchronized String generateName(boolean isConstructor,                                                  boolean forSerialization)  {      if (isConstructor) {          if (forSerialization) {              int num = ++serializationConstructorSymnum;              return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;          } else {              int num = ++constructorSymnum;              return "sun/reflect/GeneratedConstructorAccessor" + num;          }      } else {          int num = ++methodSymnum;          return "sun/reflect/GeneratedMethodAccessor" + num;      }  }

去阅读源码的话,可以看到MethodAccessorGenerator是如何一点点把Java版的MethodAccessor实现类生产出来的。也可以看到GeneratedMethodAccessor+数字这种名字是从哪里来的了,就在上面的generateName()方法里。
对本文开头的例子的A.foo(),生成的Java版MethodAccessor大致如下:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. package sun.reflect;   
  2.   
  3. public class GeneratedMethodAccessor1 extends MethodAccessorImpl {       
  4.     public GeneratedMethodAccessor1() {   
  5.         super();   
  6.     }   
  7.        
  8.     public Object invoke(Object obj, Object[] args)      
  9.         throws IllegalArgumentException, InvocationTargetException {   
  10.         // prepare the target and parameters   
  11.         if (obj == nullthrow new NullPointerException();   
  12.         try {   
  13.             A target = (A) obj;   
  14.             if (args.length != 1throw new IllegalArgumentException();   
  15.             String arg0 = (String) args[0];   
  16.         } catch (ClassCastException e) {   
  17.             throw new IllegalArgumentException(e.toString());   
  18.         } catch (NullPointerException e) {   
  19.             throw new IllegalArgumentException(e.toString());   
  20.         }   
  21.         // make the invocation   
  22.         try {   
  23.             target.foo(arg0);   
  24.         } catch (Throwable t) {   
  25.             throw new InvocationTargetException(t);   
  26.         }   
  27.     }   
  28. }  
package sun.reflect;    public class GeneratedMethodAccessor1 extends MethodAccessorImpl {          public GeneratedMethodAccessor1() {          super();      }            public Object invoke(Object obj, Object[] args)             throws IllegalArgumentException, InvocationTargetException {          // prepare the target and parameters          if (obj == null) throw new NullPointerException();          try {              A target = (A) obj;              if (args.length != 1) throw new IllegalArgumentException();              String arg0 = (String) args[0];          } catch (ClassCastException e) {              throw new IllegalArgumentException(e.toString());          } catch (NullPointerException e) {              throw new IllegalArgumentException(e.toString());          }          // make the invocation          try {              target.foo(arg0);          } catch (Throwable t) {              throw new InvocationTargetException(t);          }      }  }

就反射调用而言,这个invoke()方法非常干净(然而就“正常调用”而言这额外开销还是明显的)。注意到参数数组被拆开了,把每个参数都恢复到原本没有被Object[]包装前的样子,然后对目标方法做正常的invokevirtual调用。由于在生成代码时已经循环遍历过参数类型的数组,生成出来的代码里就不再包含循环了。
当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。
虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。
相比之下JDK 7里新的MethodHandle则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧。

到本来Java的安全机制使得不同类之间不是任意信息都可见,但Sun的JDK里开了个口,有一个标记类专门用于开后门:
Java代码 复制代码 收藏代码关于反射调用方法的一个log  [Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__] - 和申 - 和申的个人主页
  1. package sun.reflect;   
  2.   
  3. /** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and  
  4.     others, not because it actually implements an interface) is a  
  5.     marker class in the hierarchy. All subclasses of this class are  
  6.     "magically" granted access by the VM to otherwise inaccessible  
  7.     fields and methods of other classes. It is used to hold the code  
  8.     for dynamically-generated FieldAccessorImpl and MethodAccessorImpl  
  9.     subclasses. (Use of the word "unsafe" was avoided in this class's  
  10.     name to avoid confusion with {@link sun.misc.Unsafe}.) </P>  
  11.  
  12.     <P> The bug fix for 4486457 also necessitated disabling  
  13.     verification for this class and all subclasses, as opposed to just  
  14.     SerializationConstructorAccessorImpl and subclasses, to avoid  
  15.     having to indicate to the VM which of these dynamically-generated  
  16.     stub classes were known to be able to pass the verifier. </P>  
  17.  
  18.     <P> Do not change the name of this class without also changing the  
  19.     VM's code. </P> */  
  20.   
  21. class MagicAccessorImpl {   
  22. }  
package sun.reflect;    /** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and      others, not because it actually implements an interface) is a      marker class in the hierarchy. All subclasses of this class are      "magically" granted access by the VM to otherwise inaccessible      fields and methods of other classes. It is used to hold the code      for dynamically-generated FieldAccessorImpl and MethodAccessorImpl      subclasses. (Use of the word "unsafe" was avoided in this class's      name to avoid confusion with {@link sun.misc.Unsafe}.) </P>        <P> The bug fix for 4486457 also necessitated disabling      verification for this class and all subclasses, as opposed to just      SerializationConstructorAccessorImpl and subclasses, to avoid      having to indicate to the VM which of these dynamically-generated      stub classes were known to be able to pass the verifier. </P>        <P> Do not change the name of this class without also changing the      VM's code. </P> */    class MagicAccessorImpl {  }


OK,本文就记到这里吧。希望对问答频道提问那位同学有帮助。

Have fun ^_^

============================================================================

P.S. log里的"shared objects file"其实也是个有趣的话题。有机会的话也可以写写。

转载自:http://rednaxelafx.iteye.com/blog/548536

  评论这张
 
阅读(2808)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016