Dalvik的指令执行是解释器+JIT的方式,解释器就是虚拟机来对Javac编译出来的字节码,做译码、执行,而不是转化成CPU的指令集,由CPU来做译码,执行。可想而知,解释器的效率是相对较低的,所以出现了JIT(Just In Time),JIT是将执行次数较多的函数,做即时编译,在运行时刻,编译成本地目标代码,JIT可以看成是解释器的一个补充优化。再之后又出现了Art虚拟机的AOT(Ahead Of Time)模式,做静态编译,在Apk安装的时候就会做字节码的编译,从而效率直逼静态语言。
Java所有的方法都是类方法,因此Dalvik的字节码执行就两种,一是类的Method,包括静态和非静态,两者的差距也就是有没有this参数,二就是类的初始化代码,就是类加载的时候,成员变量的初始化以及显式的类初始化块代码。
其中类的初始化代码在dalvik/vm/oo/Class.cpp的dvmInitClass:
如果method是个native的函数,那么就直接调用nativeFunc这个函数指针,否则就调用dvmInterpret代码,dvmInterpret就是解释器的入口。
Java所有的方法都是类方法,因此Dalvik的字节码执行就两种,一是类的Method,包括静态和非静态,两者的差距也就是有没有this参数,二就是类的初始化代码,就是类加载的时候,成员变量的初始化以及显式的类初始化块代码。
其中类的初始化代码在dalvik/vm/oo/Class.cpp的dvmInitClass:
bool dvmInitClass(ClassObject* clazz)
{
...
dvmLockObject(self, (Object*) clazz);
...
android_atomic_release_store(CLASS_INITIALIZING,
(int32_t*)(void*)&clazz->status);
dvmUnlockObject(self, (Object*) clazz);
...
initSFields(clazz);
/* Execute any static initialization code.
*/
method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
if (method == NULL) {
LOGVV("No <clinit> found for %s", clazz->descriptor);
} else {
LOGVV("Invoking %s.<clinit>", clazz->descriptor);
JValue unused;
dvmCallMethod(self, method, NULL, &unused);
}
...
}
从代码可见,类初始化的主要代码逻辑包括:
类对象加锁,所以类的加载是单线程的
初始化static成员(initSFields)
调用<cinit>,静态初始化块
类的初始化块代码在<cinit>的成员函数里。可见Dalvik的字节码解释,本质上还是类成员函数的解释执行。
虚拟机以Method作为解释器的执行单元,其入口就统一为dvmCallMethod,该函数的定义在dalvik/vm/interp/Stack.cpp里。
void dvmCallMethod(Thread* self, const Method* method, Object* obj,
JValue* pResult, ...)
{
va_list args;
va_start(args, pResult);
dvmCallMethodV(self, method, obj, false, pResult, args);
va_end(args);
}
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
bool fromJni, JValue* pResult, va_list args)
{
...
if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
(*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
}
…
}
Java的Method有native函数和非native函数,native的函数的代码段是在so里,是本地指令集而非虚拟机的字节码。
虚拟机以Method作为解释器的执行单元,其入口就统一为dvmCallMethod,该函数的定义在dalvik/vm/interp/Stack.cpp里。
void dvmCallMethod(Thread* self, const Method* method, Object* obj,
JValue* pResult, ...)
{
va_list args;
va_start(args, pResult);
dvmCallMethodV(self, method, obj, false, pResult, args);
va_end(args);
}
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
bool fromJni, JValue* pResult, va_list args)
{
...
if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
(*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
}
…
}
如果method是个native的函数,那么就直接调用nativeFunc这个函数指针,否则就调用dvmInterpret代码,dvmInterpret就是解释器的入口。
如果把Dalvik函数执行的调用栈画出来,我们会更清楚整个流程。
public class HelloWorld {
public int foo(