上篇文章中回顾了一下Java反射相关的基础内容。这一节我们来深入研究Method类中的invoke方法,探寻它的奥秘。 注:本篇文章的所有源码都基于OpenJDK 1.8。
引入 即使没有学过反射,大家也一定会见过invoke方法。因为很多方法调用都是靠invoke方法,所以很多异常的抛出都会定位到invoke方法,比如下面的情形大家会很熟悉:1
2
3
4
5
6
java.lang.NullPointerException
at ......
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 )
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 )
at java.lang.reflect.Method.invoke(Method.java:497 )
大家在看到异常抛出时,除了想要排除Bug,是不是同时也对这个神秘的invoke乃至invoke0方法有一些好奇呢?我们下面就来揭开它神秘的面纱,探寻底层的机制。
浅析invoke过程 上一篇文章我们讲过,invoke方法用来在运行时动态地调用某个实例的方法。它的实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@CallerSensitive
public Object invoke (Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor;
if (ma == null ) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
我们根据invoke方法的实现,将其分为以下几步:
1、权限检查 invoke方法会首先检查AccessibleObject的override属性的值。AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 override的值默认是false,表示需要权限调用规则,调用方法时需要检查权限;我们也可以用setAccessible方法设置为true,若override的值为true,表示忽略权限规则,调用方法时无需检查权限(也就是说可以调用任意的private方法,违反了封装)。 如果override属性为默认值false,则进行进一步的权限检查: (1)首先用Reflection.quickCheckMemberAccess(clazz, modifiers)方法检查方法是否为public,如果是的话跳出本步;如果不是public方法,那么用Reflection.getCallerClass()方法获取调用这个方法的Class对象,这是一个native方法:1
2
@CallerSensitive
public static native Class<?> getCallerClass();
在OpenJDK的源码中找到此方法的JNI入口(Reflection.c):1
2
3
4
5
JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__
(JNIEnv *env, jclass unused)
{
return JVM_GetCallerClass(env, JVM_CALLER_DEPTH);
}
其中JVM_GetCallerClass的源码如下,有兴趣的可以研究一下(位于jvm.cpp):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
JVM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth))
JVMWrapper("JVM_GetCallerClass" );
if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) {
Klass* k = thread->security_get_caller_class(depth);
return (k == NULL ) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
}
vframeStream vfst (thread) ;
for (int n = 0 ; !vfst.at_end(); vfst.security_next(), n++) {
Method* m = vfst.method();
assert(m != NULL , "sanity" );
switch (n) {
case 0 :
if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass" );
}
case 1 :
if (!m->caller_sensitive()) {
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d" , n));
}
break ;
default :
if (!m->is_ignored_by_security_stack_walk()) {
return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());
}
break ;
}
}
return NULL ;
JVM_END
获取了这个Class对象caller后用checkAccess方法做一次快速的权限校验,其实现为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
volatile Object securityCheckCache;
void checkAccess (Class<?> caller, Class<?> clazz, Object obj, int modifiers)
throws IllegalAccessException
{
if (caller == clazz) {
return ;
}
Object cache = securityCheckCache;
Class<?> targetClass = clazz;
if (obj != null
&& Modifier.isProtected(modifiers)
&& ((targetClass = obj.getClass()) != clazz)) {
if (cache instanceof Class[]) {
Class<?>[] cache2 = (Class<?>[]) cache;
if (cache2[1 ] == targetClass &&
cache2[0 ] == caller) {
return ;
}
}
} else if (cache == caller) {
return ;
}
slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
}
首先先执行一次快速校验,一旦调用方法的Class正确则权限检查通过。 若未通过,则创建一个缓存,中间再进行一堆检查(比如检验是否为protected属性)。 如果上面的所有权限检查都未通过,那么将执行更详细的检查,其实现为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void slowCheckMemberAccess (Class<?> caller, Class<?> clazz, Object obj, int modifiers,
Class<?> targetClass)
throws IllegalAccessException
{
Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);
Object cache = ((targetClass == clazz)
? caller
: new Class<?>[] { caller, targetClass });
securityCheckCache = cache;
}
大体意思就是,用Reflection.ensureMemberAccess方法继续检查权限,若检查通过就更新缓存,这样下一次同一个类调用同一个方法时就不用执行权限检查了,这是一种简单的缓存机制。由于JMM的happens-before规则能够保证缓存初始化能够在写缓存之前发生,因此两个cache不需要声明为volatile。 到这里,前期的权限检查工作就结束了。如果没有通过检查则会抛出异常,如果通过了检查则会到下一步。
2、调用MethodAccessor的invoke方法 Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。 首先要了解Method对象的基本构成,每个Java方法有且只有一个Method对象作为root,它相当于根对象,对用户不可见。当我们创建Method对象时,我们代码中获得的Method对象都相当于它的副本(或引用)。root对象持有一个MethodAccessor对象,所以所有获取到的Method对象都共享这一个MethodAccessor对象,因此必须保证它在内存中的可见性。root对象其声明及注释为:1
2
3
4
5
6
7
8
private volatile MethodAccessor methodAccessor;
private Method root;
那么MethodAccessor到底是个啥玩意呢?
1
2
3
4
5
6
7
8
9
10
java.lang.reflect.Method.invoke(). Each Method object is
configured with a (possibly dynamically-generated) class which
implements this interface.
*/
public interface MethodAccessor {
public Object invoke (Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException;
}
可以看到MethodAccessor是一个接口,定义了invoke方法。分析其Usage可得它的具体实现类有:
sun.reflect.DelegatingMethodAccessorImpl
sun.reflect.MethodAccessorImpl
sun.reflect.NativeMethodAccessorImpl
第一次调用一个Java方法对应的Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没有创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()完成反射调用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private MethodAccessor acquireMethodAccessor () {
MethodAccessor tmp = null ;
if (root != null ) tmp = root.getMethodAccessor();
if (tmp != null ) {
methodAccessor = tmp;
} else {
tmp = reflectionFactory.newMethodAccessor(this );
setMethodAccessor(tmp);
}
return tmp;
}
可以看到methodAccessor实例由reflectionFactory对象操控生成,它在AccessibleObject下的声明如下:
1
2
3
4
5
6
static final ReflectionFactory reflectionFactory =
AccessController.doPrivileged(
new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
再研究一下sun.reflect.ReflectionFactory类的源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class ReflectionFactory {
private static boolean initted = false ;
private static Permission reflectionFactoryAccessPerm
= new RuntimePermission("reflectionFactoryAccess" );
private static ReflectionFactory soleInstance = new ReflectionFactory();
private static volatile LangReflectAccess langReflectAccess;
private static boolean noInflation = false ;
private static int inflationThreshold = 15 ;
public MethodAccessor newMethodAccessor (Method method) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
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;
}
}
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<Void>() {
public Void run () {
if (System.out == null ) {
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 new RuntimeException("Unable to parse property sun.reflect.inflationThreshold" , e);
}
}
initted = true ;
return null ;
}
});
}
}
观察前面的声明部分的注释,我们可以发现一些有趣的东西。就像注释里说的,实际的MethodAccessor实现有两个版本,一个是Java版本,一个是native版本,两者各有特点。初次启动时Method.invoke()和Constructor.newInstance()方法采用native方法要比Java方法快3-4倍,而启动后native方法又要消耗额外的性能而慢于Java方法。也就是说,Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了尽可能地减少性能损耗,HotSpot JDK采用“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版本。 这项优化是从JDK 1.4开始的。
研究ReflectionFactory.newMethodAccessor()生产MethodAccessor对象的逻辑,一开始(native版)会生产NativeMethodAccessorImpl和DelegatingMethodAccessorImpl两个对象。 DelegatingMethodAccessorImpl的源码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
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()方法被调用时,程序调用计数器都会增加1,看看是否超过阈值;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。 到这里,我们已经追寻到native版的invoke方法在Java一侧声明的最底层 - invoke0了,下面我们将深入到HotSpot JVM中去研究其具体实现。
寻根溯源 - 在JVM层面探究invoke0方法 invoke0方法是一个native方法,它在HotSpot JVM里调用JVM_InvokeMethod函数:1
2
3
4
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);
}
openjdk/hotspot/src/share/vm/prims/jvm.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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: openjdk/hotspot/src/share/vm/runtime/reflection.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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_Klass(mirror));
Method* 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);
}
这里面又会涉及到Java的对象模型(klass和oop),以后继续补充。(留坑 )
寻根溯源 - Java版的实现 Java版MethodAccessor的生成使用MethodAccessorGenerator实现,由于代码太长,这里就不贴代码了,只贴一下开头的注释:1
2
3
4
5
6
7
sun.reflect.ConstructorAccessor objects using bytecodes to
implement reflection. A java.lang.reflect.Method or
java.lang.reflect.Constructor object can delegate its invoke or
newInstance method to an accessor using native code or to one
generated by this class. (Methods and Constructors were merged
together in this class to ensure maximum code sharing.) */
这里运用了asm动态生成字节码技术(sun.reflect.ClassFileAssembler),原理比较复杂,后面讲到AOP要用到asm技术的时候再深入了解一下吧。
本篇总结 简单地画了个图表示invoke方法的过程,日后再更时序图:
番外篇
MagicAccessorImpl是什么鬼?
原本Java的安全机制使得不同类之间不是任意信息都可见,但JDK里面专门设了个MagicAccessorImpl标记类开了个后门来允许不同类之间信息可以互相访问(由JVM管理):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 {
}
@CallerSensitive注解又是什么鬼?
详见:JEP 176: Mechanical Checking of Caller-Sensitive Methods
Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing hand-maintained list of caller-sensitive methods with a mechanism that accurately identifies such methods and allows their callers to be discovered reliably.
JDK 1.8才引进了这个注解,因此在老版本的反射实现里并没有这个玩意。这是它的定义:1
2
3
4
5
6
7
8
9
10
11
* A method annotated @CallerSensitive is sensitive to its calling class,
* via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass},
* or via some equivalent.
*
* @author John R. Rose
*/
@Retention (RetentionPolicy.RUNTIME)
@Target ({METHOD})
public @interface CallerSensitive {
}
简而言之,用@CallerSensitive
注解修饰的方法从一开始就知道具体调用它的对象,这样就不用再经过一系列的检查才能确定具体调用它的对象了。它实际上是调用sun.reflect.Reflection.getCallerClass
方法。
Reflection类位于调用栈中的0帧位置,sun.reflect.Reflection.getCallerClass()
方法返回调用栈中从0帧开始的第x帧中的类实例。该方法提供的机制可用于确定调用者类,从而实现“感知调用者(Caller Sensitive)”的行为,即允许应用程序根据调用类或调用栈中的其它类来改变其自身的行为。
Reference