要想深入地理解 art 虚拟机,那么理解 Java 方法在虚拟机当中是如何执行的是必不可少的一环。
本篇从 Native 函数调用 Java 函数角度来探讨一下 Java 函数在 art 虚拟机当中的执行。(基于 Android 8.1)
首先,我们用 gdb 将断点打在 art_quick_invoke_stub,观察一下 Native 函数 -> Java 函数的调用栈: 
我们可以看到 Native 函数调用 Java 函数最开始是要调用 env->CallBooleanMethod:
(gdb) f 6 #6 0x000000798f46c2d0 in JavaBBinder::onTransact (this=0x79032efa80, code=4, data=..., reply=0x78f55fd1c0, flags=17) at frameworks/base/core/jni/android_util_Binder.cpp:312 312 jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, (gdb) list 307 const int32_t strict_policy_before = thread_state->getStrictModePolicy(); 308 309 //printf("Transact from %p to Java code sending: ", this); 310 //data.print(); 311 //printf("\n"); 312 jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, 313 code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags); 314 315 if (env->ExceptionCheck()) { 316 jthrowable excep = env->ExceptionOccurred(); 1、CallBooleanMethod
我们来看一下 CallBooleanMethod 定义:
libnativehelper/include_jni/jni.h
594#define CALL_TYPE_METHOD(_jtype, _jname) \ 595 _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \ 596 { \ 597 _jtype result; \ 598 va_list args; \ 599 va_start(args, methodID); \ 600 result = functions->Call##_jname##MethodV(this, obj, methodID, \ 601 args); \ 602 va_end(args); \ 603 return result; \ 604 } 605#define CALL_TYPE_METHODV(_jtype, _jname) \ 606 _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \ 607 va_list args) \ 608 { return functions->Call##_jname##MethodV(this, obj, methodID, args); } 609#define CALL_TYPE_METHODA(_jtype, _jname) \ 610 _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \ 611 jvalue* args) \ 612 { return functions->Call##_jname##MethodA(this, obj, methodID, args); } 613 614#define CALL_TYPE(_jtype, _jname) \ 615 CALL_TYPE_METHOD(_jtype, _jname) \ 616 CALL_TYPE_METHODV(_jtype, _jname) \ 617 CALL_TYPE_METHODA(_jtype, _jname) 618 619 CALL_TYPE(jobject, Object) 620 CALL_TYPE(jboolean, Boolean) 621 CALL_TYPE(jbyte, Byte) 622 CALL_TYPE(jchar, Char) 623 CALL_TYPE(jshort, Short) 624 CALL_TYPE(jint, Int) 625 CALL_TYPE(jlong, Long) 626 CALL_TYPE(jfloat, Float) 627 CALL_TYPE(jdouble, Double) 可以看到其是通过宏来定义的,CALL_TYPE(jboolean, Boolean) 即相当于
595 jboolean CallBooleanMethod(jobject obj, jmethodID methodID, ...) 596 { 597 jboolean result; 598 va_list args; 599 va_start(args, methodID); 600 result = functions->CallBooleanMethodV(this, obj, methodID, 601 args); 602 va_end(args); 603 return result; 604 } 可以看到这里的作用主要是调整了一下参数,然后调用 JNIInvokeInterface 的 CallBooleanMethodV 方法,下面我们看一下 CallBooleanMethodV 的实现:
art/runtime/jni_internal.cc
817 static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { 818 CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); 819 CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); 820 ScopedObjectAccess soa(env); 821 return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); 822 } 需要注意的是 ScopedObjectAccess soa(env); 这一步 Thread 会完成到 kRunnable 状态的切换(通过一系列的构造函数)并且其会 Shared Locks::mutator_lock_
2、InvokeVirtualOrInterfaceWithVarArgs
art/runtime/reflection.cc
552JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, 553 jobject obj, jmethodID mid, va_list args) { 561 ... 562 ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj); 563 ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid)); ... 570 uint32_t shorty_len = 0; 571 const char* shorty = 572 method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); 573 JValue result; 574 ArgArray arg_array(shorty, shorty_len); 575 arg_array.BuildArgArrayFromVarArgs(soa, receiver, args); 576 InvokeWithArgArray(soa, method, &arg_array, &result, shorty); ... 581 return result; 582} 可以看到,这里主要做了四件事:
- get shorty
- prepare result
- 构造参数并将其放在 arg_array 中
- 调用 InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
下面看一下是如何构造参数的:
art/runtime/reflection.cc
101 void BuildArgArrayFromVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, 102 ObjPtr<mirror::Object> receiver, 103 va_list ap) 104 REQUIRES_SHARED(Locks::mutator_lock_) { 105 // Set receiver if non-null (method is not static) 106 if (receiver != nullptr) { 107 Append(receiver); 108 } 109 for (size_t i = 1; i < shorty_len_; ++i) { 110 switch (shorty_[i]) { 111 case 'Z': 112 case 'B': 113 case 'C': 114 case 'S': 115 case 'I': 116 Append(va_arg(ap, jint)); 117 break; 118 case 'F': 119 AppendFloat(va_arg(ap, jdouble)); 120 break; 121 case 'L': 122 Append(soa.Decode<mirror::Object>(va_arg(ap, jobject))); 123 break; 124 case 'D': 125 AppendDouble(va_arg(ap, jdouble)); 126 break; 127 case 'J': 128 AppendWide(va_arg(ap, jlong)); 129 break; 130#ifndef NDEBUG 131 default: 132 LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; 133#endif 134 } 135 } 136 } 需要注意下面几个点:
- 如果 receiver 不为 null,即 method 是非 static 的,那么 arg_array 中放入的第一个参数即为 receiver
- 会根据参数对应的 shorty 中的值来决定以何种方式将参数放入 arg_array 中
下面看一下 InvokeWithArgArray 的实现:
446static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, 447 ArtMethod* method, ArgArray* arg_array, JValue* result, 448 const char* shorty) 449 REQUIRES_SHARED(Locks::mutator_lock_) { 450 uint32_t* args = arg_array->GetArray(); 451 if (UNLIKELY(soa.Env()->check_jni)) { 452 CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args); 453 } 454 method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); 455} 这里没什么可说的,就是调用 ArtMethod 的 Invoke 方法
3、art::ArtMethod::Invoke
art/runtime/art_method.cc
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) { // Push a transition back into managed code onto the linked list in thread. ManagedStack fragment; self->PushManagedStackFragment(&fragment); Runtime* runtime = Runtime::Current(); // Call the invoke stub, passing everything as arguments. // If the runtime is not yet started or it is required by the debugger, then perform the // Invocation by the interpreter, explicitly forcing interpretation over JIT to prevent // cycling around the various JIT/Interpreter methods that handle method invocation. if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) { ... } else { DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); constexpr bool kLogInvocationStartAndReturn = false; bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr; if (LIKELY(have_quick_code)) { ... if (!IsStatic()) { (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); } else { (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty); } ... } else { LOG(INFO) << "Not invoking '" << PrettyMethod() << "' code=null"; if (result != nullptr) { result->SetJ(0); } } } // Pop transition. self->PopManagedStackFragment(fragment); } 可以看到,正常情况下其会根据 method 是否为 static 来分别调用 (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty) 和 (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty) 方法,this 为 ArtMethod 对象指针
4、art_quick_invoke_stub
详细实现部分现在功力不够,之后有时间再分析;
可以看到无论是 art_quick_invoke_stub 还是 art_quick_invoke_static_stub 最终都会调用 INVOKE_STUB_CALL_AND_RETURN,其定义为:
arch/arm64/quick_entrypoints_arm64.S
.macro INVOKE_STUB_CALL_AND_RETURN REFRESH_MARKING_REGISTER // load method-> METHOD_QUICK_CODE_OFFSET ldr x9, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64] // Branch to method. blr x9 ... ret .endm 通过前面的分析,我们知道:
- x0 中是想要调用的方法的 ArtMethod 的地址
- ART_METHOD_QUICK_CODE_OFFSET_64 为 40,即 0x28
通过 gdb 可以看到:
(gdb) p (('art::ArtMethod'*)0)->ptr_sized_fields_ Cannot access memory at address 0x18 (gdb) ptype 'art::ArtMethod::PtrSizedFields' type = struct art::ArtMethod::PtrSizedFields { art::mirror::MethodDexCacheType *dex_cache_resolved_methods_; void *data_; void *entry_point_from_quick_compiled_code_; } (gdb) p (('art::ArtMethod'*)0)->ptr_sized_fields_.entry_point_from_quick_compiled_code_ Cannot access memory at address 0x28 由此可知,这里是要跳转到对应 ArtMethod 的 entry_point_from_quick_compiled_code_ 处
通过上面的分析,从一个 Native 函数调用一个 Java 函数可以总结为以下几个步骤: 