前言
ART 虚拟机执行 Java 方法主要有两种模式:quick code 模式和 Interpreter 模式
- quick code 模式:执行 arm 汇编指令
- Interpreter 模式:由解释器解释执行 Dalvik 字节码
本篇文章就来讲一下,Interpreter 模式是如何运行的(基于 Android 8.1)
一、 Interpreter 模式
点击查看大图
上图是将断点打在 art_quick_invoke_stub 时出现的一段 backtraces,这段 backtraces 很好地描述出了 Interpreter 模式是如何运转的,以及 quick code 模式与 Interpreter 模式之间是如何切换的
1.1 art_quick_to_interpreter_bridge
从 f 19、f 18 可以看到由 quick code 模式进入 Interpreter 模式需要通过 art_quick_to_interpreter_bridge 这个 bridge,
点击查看大图
从 f 18 可以看到,artQuickToInterpreterBridge 会通过调用 interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame); 来进入 Interpreter 模式,查看一下 EnterInterpreterFromEntryPoint 的定义:
JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame) {
DCHECK_EQ(self, Thread::Current());
bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return JValue();
}
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod());
}
return Execute(self, code_item, *shadow_frame, JValue());
}
可以看到其会调用 Execute() 函数,结合上面的 backtraces,我们可以将 Execute() 函数看作是 Interpreter 模式的起点
1.2 Execute()
art/runtime/interpreter/interpreter.cc
enum InterpreterImplKind {
kSwitchImplKind, // Switch-based interpreter implementation.
kMterpImplKind // Assembly interpreter
};
static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; // 默认使用 Mterp 类型的实现
static inline JValue Execute(
Thread* self,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
...
if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization.
if (kIsDebugBuild) {
self->AssertNoPendingException();
}
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod *method = shadow_frame.GetMethod();
...
if (!stay_in_interpreter) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod());
if (jit->CanInvokeCompiledCode(method)) { // 1、jit 不为 nullptr,并且 jit 编译出了对应的 quick code,那么 ArtInterpreterToCompiledCodeBridge
JValue result;
// Pop the shadow frame before calling into compiled code.
self->PopShadowFrame();
// Calculate the offset of the first input reg. The input registers are in the high regs.
// It's ok to access the code item here since JIT code will have been touched by the
// interpreter and compiler already.
uint16_t arg_offset = code_item->registers_size_ - code_item->ins_size_;
ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result);
// Push the shadow frame back as the caller will expect it.
self->Pu