一,apk以进程的形式运行,进程的创建是由zygote。
参考文章《深入理解Dalvik虚拟机- Android应用进程启动过程分析》
二,进程运行起来之后,初始化JavaVM
参考文章《深入理解ART虚拟机—虚拟机的启动》
三,JavaVM创建之后,我们就有了JNINativeInterface,里面包含了所有的Java接口,比如FindClass,NewObject,CallObjectMethod等
参考文章《深入理解ART虚拟机—虚拟机的启动》
四,Java的运行时的功能简单来说分为:类的加载和函数Method的执行
参考文章《深入理解Dalvik虚拟机- 解释器的运行机制》
1 虚拟机机制
1.1 Java虚拟机
java virtual machine(java虚拟机的运行机制)
http://webzixue.iteye.com/blog/1227802
Java语言使用模式 Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。
Java虚拟机体系结构 Java虚拟机由五个部分组成:一组指令集、一组寄存器、一个栈、一个无用单元收集堆(Garbage-collected-heap)、一个方法区域。这五部分是Java虚拟机的逻辑成份,不依赖任何实现技术或组织方式,但它们的功能必须在真实机器上以某种方式实现。
1.1.1 Java指令集
Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器、子程序转移等。Java指令集相当于Java程序的汇编语言。
Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。
虚拟机的内层循环的执行过程如下:
do{
取一个操作符字节;
根据操作符的值执行一个动作;
}while(程序未结束)
由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为:
第一个字节*256 第二个字节字节码指令流一般只是字节对齐的。指令tabltch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。
1.1.2 寄存器
Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。 Java虚拟机的寄存器有四种:
• pc: Java程序计数器。
• optop: 指向操作数栈顶端的指针。
• frame: 指向当前执行方法的执行环境的指针。
• vars: 指向当前执行方法的局部变量区第一个变量的指针。
1.1.3 栈
Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。
(1)局部变量区 每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n 1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。
(2)运行环境区 在运行环境中包含的信息用于动态链接,正常的方法返回以及异常传播。
• 动态链接
运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。
• 正常的方法返回
如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。
• 异常和错误传播
异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。
• 程序使用了throw语句。
当异常发生时,Java虚拟机采取如下措施:
◦ 检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围、能够处理的异常类型、以及处理异常的代码块地址。
◦ 与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。
◦ 由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
◦ 如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误传播将被继续下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
(3)操作数栈区 机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的,它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数并保存操作的结果。例如,iadd指令将两个整数相加,相加的两个整数应该是操作数栈顶的两个字,这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。
每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。
1.1.4 无用单元收集堆
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java语言具有无用单元收集能力:它不给程序员显式释放对象的能力。Java不规定具体使用的无用单元收集算法,可以根据系统的需求使用各种各样的算法。
1.1.5 方法区
方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在无用单元收集堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。
1.2 Dalvik虚拟机-Android应用进程启动过程
参考文章《深入理解Dalvik虚拟机- Android应用进程启动过程分析》
Android的应用进程启动是apk在manifest里申明的Activity、Service、BroadcastReceiver等组件被调起时而触发的。我们以Activity为例,当点击桌面上的应用图标时,桌面会调用startActivity,启动manifest里申明的相应Launcher的Activity,而Activity的startActivity会通过Binder调用来到ActivityManagerService(AMS)里。AMS是system_server的一个服务,负责管理Android系统的所有Activity的栈,逻辑比较复杂,在这里就不详细分析,以后专门写AMS的专题。AMS里startActivity的时候,如果发现这个应用进程没有启动,那么就会通过Zygote创建出这个进程。
frameworks/base/services/Java/com/android/server/am/ActivityManagerService.java:
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) {
...
...
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, null);
...
...
}
显然是通过调用Process.start来启动进程,”android.app.ActivityThread” 这个参数是整个进程启动的入口类,后续的分析可以看到,进程被fork出来之后,就会调用android.app.ActivityThread的main函数。
frameworks/base/core/java/android/os/Process.java:
public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG, "Starting VM process through Zygote failed");
throw new RuntimeException("Starting VM process through Zygote failed", ex);
}
}
private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] extraArgs) throws ZygoteStartFailedEx {
synchronized(Process.class) {
ArrayList argsForZygote = new ArrayList();
// --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" uid);
argsForZygote.add("--setgid=" gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
argsForZygote.add("--mount-external-multiuser");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
argsForZygote.add("--mount-external-multiuser-all");
}
argsForZygote.add("--target-sdk-version=" targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i ) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" seInfo);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " abi);
}
调用startViaZygote函数,从代码可以看到,Zygote创建进程是socket跨进程的调用。通过LocalSocket通信,来完成进程的创建,所以这里的Process.start只是一个Client端的调用,实际是由Server端的接收到消息后处理的。
Server端是app_process这个进程里,这是个常驻的系统服务。
frameworks/base/cmds/app_process/app_main.cpp
class AppRuntime : public AndroidRuntime
{
}
AppRuntime继承自AndroidRuntime,AndroidRuntime类是libandroid_runtime.so里导出的,frameworks/base/core/jni/AndroidRuntime.cpp。
app_process 这个进程在init.rc里会创建,
#init.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
app_process以zygote作为进程名,这个adb shell进入手机ps一下就可以看到zygote。
这个进程的main函数里有如下初始化代码片段:
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv ;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc) {
const char* arg =argv[i ];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg,"--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg,"--start-system-server") == 0) {
startSystemServer= true;
} else if (strcmp(arg,"--application") == 0) {
application = true;
} else if(strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" :"");
} else if (className) {
// Remainder of argsget passed to startup class main()
runtime.mClassName =className;
runtime.mArgC = argc -i;
runtime.mArgV = argv i;
runtime.start("com.android.internal.os.RuntimeInit", application ?"application" : "tool");
}
第一个if是正常的Zygote进程启动,执行AndroidRuntime::start,会调用ZygoteInit的类main函数。app_process的初始化就在ZygoteInit里,监听本地的socket,接收ZygoteClient的请求,当有请求来的时候,调用ZygoteConnection::runOnce,从而调用Zygote.forkAndSpecialize来创建新的进程,并且调用RuntimeInit.zygoteInit做进程的初始化,初始化过程就会调用ActivityThread.main
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
...
...
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
...
}
第二个当指定了className的时候,则用com.android.internal.os.RuntimeInit作为入口,指定className的场景是用am命令创建的进程的时候:
exec app_process base/bin com.android.commands.am.Am "
而我们正常的Zygote进程则走的是ZygoteInit。AndroidRuntime.start函数会调用startVm,frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM **pJavaVM, JNIEnv **pEnv)
{
...
init Args.version = JNI_VERSION_1_4;
init Args.options = mOptions.editArray();
init Args.nOptions = mOptions.size();
init Args.ignoreUnrecognized = JNI_FALSE;
/*
* Initialize the VM.
*
* The JavaVM* isessentially per-process, and the JNIEnv* is per-thread.
* If this callsucceeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failedn");
goto bail;
}
}
startVm会初始化JavaVm和JNIEnv,最终是通过JNI_CreateJavaVM实现的,JNI_CreateJavaVM是个接口,不同的虚拟机有不同的实现。
Dalvik虚拟机相关的代码主要在下面几个目录下:
• libcore/libdvm
• libcore/dalvik
• dalvik/vm/
Dalvik的JNI_CreateJavaVM在dalvik/vm/Jni.cpp 里实现:
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void*vm_args) {
const JavaVMInitArgs *args = (JavaVMInitArgs*) vm_args;
if(dvmIsBadJniVersion(args->version)) {
ALOGE("BadJNI version passed to CreateJavaVM: %d", args->version);
return JNI_EVERSION;
}
// TODO: don't allowcreation of multiple VMs -- one per customer for now
/* zero globals; notstrictly necessary the first time a VM is started */
memset(&gDvm, 0,sizeof(gDvm));
/*
* Set up structuresfor JNIEnv and VM.
*/
JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
pVM->funcTable = &gInvokeInterface;
pVM->envList = NULL;
dvmInitMutex(&pVM->envListLock);
UniquePtr argv(new const char*[args->nOptions]);
memset(argv.get(), 0,sizeof(char*) * (args->nOptions));
/*
* Convert JNI args toargv.
*
* We have to pull outvfprintf/exit/abort, because they use the
*"extraInfo" field to pass function pointer "hooks" in. We also
* look for the-Xcheck:jni stuff here.
*/
int argc = 0;
for (int i = 0; i nOptions; i ) {
const char* optStr= args->options[i].optionString;
if (optStr ==NULL) {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d wasNULLn", i);
return JNI_ERR;
} else if(strcmp(optStr, "vfprintf") == 0) {
gDvm.vfprintfHook = (int (*)(FILE *, const char*,va_list))args->options[i].extraInfo;
} else if(strcmp(optStr, "exit") == 0) {
gDvm.exitHook= (void (*)(int)) args->options[i].extraInfo;
} else if(strcmp(optStr, "abort") == 0) {
gDvm.abortHook= (void (*)(void))args->options[i].extraInfo;
} else if(strcmp(optStr, "sensitiveThread") == 0) {
gDvm.isSensitiveThreadHook = (bool(*)(void))args->options[i].extraInfo;
} else if(strcmp(optStr, "-Xcheck:jni") == 0) {
gDvmJni.useCheckJni = true;
} else if(strncmp(optStr, "-Xjniopts:", 10) == 0) {
char* jniOpts= strdup(optStr 10);
size_t jniOptCount = 1;
for (char* p =jniOpts; *p != 0; p) {
if (*p ==',') {
jniOptCount;
*p =0;
}
}
char* jniOpt = jniOpts;
for (size_t i= 0; i < jniOptCount; i) {
if(strcmp(jniOpt, "warnonly") == 0) {
gDvmJni.warnOnly = true;
} else if(strcmp(jniOpt, "forcecopy") == 0) {
gDvmJni.forceCopy = true;
} else if(strcmp(jniOpt, "logThirdPartyJni") == 0) {
gDvmJni.logThirdPartyJni = true;
} else{
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjnioptsoption '%s'n",
jniOpt);
free(pVM);
free(jniOpts);
return JNI_ERR;
}
jniOpt = strlen(jniOpt) 1;
}
free(jniOpts);
} else {
/* regularoption */
argv[argc ] = optStr;
}
}
if(gDvmJni.useCheckJni) {
dvmUseCheckedJniVm(pVM);
}
if (gDvmJni.jniVm !=NULL) {
dvmFprintf(stderr,"ERROR: Dalvik only supports one VM per processn");
free(pVM);
return JNI_ERR;
}
gDvmJni.jniVm = (JavaVM*) pVM;
/*
* Create a JNIEnv forthe main thread. We need to havesomething set up
* here because someof the class initialization we do when starting
* up the VM will callinto native code.
*/
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
/* Initialize VM.*/
gDvm.initializing = true;
std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
gDvm.initializing =false;
if (!status.empty()){
free(pEnv);
free(pVM);
ALOGW("CreateJavaVM failed: %s", status.c_str());
return JNI_ERR;
}
/*
* Success! Return stuff to caller.
*/
dvmChangeStatus(NULL,THREAD_NATIVE);
*p_env = (JNIEnv*)pEnv;
*p_vm = (JavaVM*)pVM;
ALOGV("CreateJavaVM succeeded");
return JNI_OK;
}
malloc完了之后,会调用dvmStartup完成虚拟机的启动,dvmStartup定义在dalvik/vm/Init.cpp里:
/*
* VM initialization. Pass in any options provided on the commandline.
* Do not pass in theclass name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, JNIEnv* pEnv)
{
ScopedShutdown scopedShutdown;
assert(gDvm.initializing);
ALOGV("VM initargs (%d):", argc);
for (int i = 0; i
ALOGV(" %d: '%s'", i, argv[i]);
}
setCommandLineDefaults();
/*
* Process the optionflags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0) {
if (cc < 0){
dvmFprintf(stderr, "n");
usage("dalvikvm");
}
return "syntax error";
}
#if WITH_EXTRA_GC_CHECKS > 1
/* only"portable" interp has the extra goodies */
if (gDvm.executionMode!= kExecutionModeInterpPortable) {
ALOGI("Switching to 'portable' interpreter for GCchecks");
gDvm.executionMode= kExecutionModeInterpPortable;
}
#endif
/* Configure groupscheduling capabilities */
if(!access("/dev/cpuctl/tasks", F_OK)) {
ALOGV("Usingkernel group scheduling");
gDvm.kernelGroupScheduling = 1;
} else {
ALOGV("Usingkernel scheduler policies");
}
/* configure signalhandling */
if(!gDvm.reduceSignals)
blockSignals();
/* verify system pagesize */
if(sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
return StringPrintf("expected page size %d, got %d", SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
}
/* mterp setup */
ALOGV("UsingexecutionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();
/*
* Initializecomponents.
*/
dvmQuasiAtomicsStartup();
if(!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()){
return "dvmGcStartup failed";
}
if (!dvmThreadStartup()){
return "dvmThreadStartup failed";
}
if(!dvmInlineNativeStartup()) {
return "dvmInlineNativeStartup";
}
if(!dvmRegisterMapStartup()) {
return "dvmRegisterMapStartup failed";
}
if(!dvmInstanceofStartup()) {
return "dvmInstanceofStartup failed";
}
if(!dvmClassStartup()) {
return "dvmClassStartup failed";
}
/*
* At this point, thesystem is guaranteed to be sufficiently
* initialized that wecan look up classes and class members. This
* call populates thegDvm instance with all the class and member
* references that theVM wants to use directly.
*/
if(!dvmFindRequiredClassesAndMembers()) {
return "dvmFindRequiredClassesAndMembers failed";
}
if(!dvmStringInternStartup()) {
return "dvmStringInternStartup failed";
}
if (!dvmNativeStartup()){
return "dvmNativeStartup failed";
}
if(!dvmInternalNativeStartup()) {
return "dvmInternalNativeStartup failed";
}
if (!dvmJniStartup()){
return "dvmJniStartup failed";
}
if (!dvmProfilingStartup()){
return "dvmProfilingStartup failed";
}
/*
* Create a table ofmethods for which we will substitute an "inline"
* version forperformance.
*/
if(!dvmCreateInlineSubsTable()) {
return "dvmCreateInlineSubsTable failed";
}
/*
* Miscellaneous classlibrary validation.
*/
if(!dvmValidateBoxClasses()) {
return "dvmValidateBoxClasses failed";
}
/*
* Do the last bits ofThread struct initialization we need to allow
* JNI calls to work.
*/
if(!dvmPrepMainForJni(pEnv)) {
return "dvmPrepMainForJni failed";
}
/*
* Explicitlyinitialize java.lang.Class. This doesn'thappen
* automaticallybecause it's allocated specially (it's an instance
* of itself). Must happen before registration of systemnatives,
* which make somecalls that throw assertions if the classes they
* operate on aren'tinitialized.
*/
if(!dvmInitClass(gDvm.classJavaLangClass)) {
return "couldn't initialized java.lang.Class";
}
/*
* Register the systemnative methods, which are registered through JNI.
*/
if(!registerSystemNatives(pEnv)) {
return "couldn't register system natives";
}
/*
* Do some"late" initialization for the memory allocator. This may
* allocate storageand initialize classes.
*/
if (!dvmCreateStockExceptions()){
return "dvmCreateStockExceptions failed";
}
/*
* At this point, theVM is in a pretty good state. Finishprep on
* the main thread(specifically, create a java.lang.Thread object to go
* along with our Thread struct). Note we will probably be executing
* some interpretedclass initializer code in here.
*/
if(!dvmPrepMainThread()) {
return "dvmPrepMainThread failed";
}
/*
* Make sure wehaven't accumulated any tracked references. The main
* thread should bestarting with a clean slate.
*/
if(dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) !=0)
{
ALOGW("Warning: tracked references remainpost-initialization");
dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable,"MAIN");
}
/* general debuggingsetup */
if(!dvmDebuggerStartup()) {
return "dvmDebuggerStartup failed";
}
if(!dvmGcStartupClasses()) {
return "dvmGcStartupClasses failed";
}
/*
* Init for eitherzygote mode or non-zygote mode. The keydifference
* is that we don'tstart any additional threads in Zygote mode.
*/
if (gDvm.zygote){
if (!initZygote()){
return "initZygote failed";
}
dvmPostInitZygote();
} else {
if(!dvmInitAfterZygote()) {
return "dvmInitAfterZygote failed";
}
}
#ifndef NDEBUG
if(!dvmTestHash())
ALOGE("dvmTestHash FAILED");
if (false /*noisy!*/&& !dvmTestIndirectRefTable())
ALOGE("dvmTestIndirectRefTable FAILED");
#endif
if(dvmCheckException(dvmThreadSelf())) {
dvmLogExceptionStackTrace();
return "Exception pending at end of VM initialization";
}
scopedShutdown.disarm();
return "";
}
dvmStartup调用了很多初始化接口,比如dvmClassStartup:
/*
* Initialize thebootstrap class loader.
*
* Call this after thebootclasspath string has been finalized.
*/
bool dvmClassStartup()
{
/* make this arequirement -- don't currently support dirs in path */
if(strcmp(gDvm.bootClassPathStr, ".") == 0) {
ALOGE("ERROR:must specify non-'.' bootclasspath");
return false;
}
gDvm.loadedClasses = dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if(gDvm.pBootLoaderAlloc == NULL)
return false;
if (false) {
linearAllocTests();
exit(0);
}
/*
* Class serialnumber. We start with a high value tomake it distinct
* in binary dumps(e.g. hprof).
*/
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
/*
* Set up the tablewe'll use for tracking initiating loaders for
* early classes.
* If it's NULL, wejust fall back to the InitiatingLoaderList in the
* ClassObject, soit's not fatal to fail this allocation.
*/
gDvm.initiatingLoaderList = (InitiatingLoaderList*) calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/*
* Create the initialclasses. These are the first objects constructed
* within the nascentVM.
*/
if(!createInitialClasses()) {
return false;
}
/*
* Process thebootstrap class path. This means openingthe specified
* DEX or Jar filesand possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath== NULL)
return false;
return true;
}
这个类初始化gDvm的bootClassPath,这样就能执行最早看到的ActivityThread的main函数。具体的调用栈为:ActivityThread.main-> ActivityThread.attach -> ActivityThread.bindApplication ->Activity.handleBindApplication.
到目前的分析,应用的ClassLoader还是BootClassPath,只包含了Java和Android的类,Apk自身的类是找不到的,会报ClassNotFound,接下来就是介绍ClassLoader的加载过程。
handleBindApplication会初始化ApplicationInfo对象,通getPackageInfo初始化LoadedApk,而LoadedApk则会创建这个apk对应的ClassLoader,这个ClassLoader是集成自BaseDexClassLoader,加载了apk的dex。
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true);
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try{
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();
}catch(Exception e) {
throw new RuntimeException("Unable to instantiate instrumentation data.instrumentationName ": " e.toString(), e);
}
ContextImpl和Instrumentation的ClassLoader都已经初始化为apk对应的BaseDexClassLoader,在这之后的类加载,都会从这个ClassLoader对象里找。ClassLoader是个树状结构,查找规则是先从父节点查找,如果类已经加载,则直接返回加载好的Class。类的加载时机有两个,一个是new操作符创建对象的,一个是直接调用ClassLoader的loadClass的时候,new操作符的代码在dalvik解释器里,我们下一个专题会讲,最后会调用dvmResolveClass(dalvik/vm/oo/Resolve.cpp)来加载类。loadClass的实现如下:
libcore/libdvm/src/main/java/java/lang/ClassLoader.java
protected Class<?> loadClass(String className, booleanresolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null){
try {
clazz = parent.loadClass(className, false);
} catch(ClassNotFoundException e) {
// Don'twant to see this.
}
if (clazz ==null) {
clazz = findClass(className);
}
}
return clazz;
}
它首先看自己的parent是否已经加载过class了,加载了就返回,没有就调用BaseDexClassLoader的findClass。
@Override
protected Class findClass(String name) throws ClassNotFoundException {
List suppressedExceptions = new ArrayList();
Class c = pathList.findClass(name, suppressedExceptions);
if(c ==null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class "" name "" on path: " pathList);
for(Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
BaseDexClassLoader的findClass,实际是调用了DexPathList的findClass。上面的整个过程就是Android的apk从启动,一直到ClassLoader被初始化完,之后就是走Android的Activity正常生命周期了。
下篇介绍一下Dalvik虚拟机的解析器的工作原理。
1.3 ART虚拟机运行机制
前面两篇文章介绍了ART的启动过程,而在启动之后,我们感兴趣的就是ART是怎么运行的。回顾一下虚拟机系列的前面几篇文章,我们可以理一下思路:
一,apk以进程的形式运行,进程的创建是由zygote。
参考文章《深入理解Dalvik虚拟机- Android应用进程启动过程分析》
二,进程运行起来之后,初始化JavaVM
参考文章《深入理解ART虚拟机—虚拟机的启动》
三,JavaVM创建之后,我们就有了JNINativeInterface,里面包含了所有的Java接口,比如FindClass,NewObject,CallObjectMethod等
参考文章《深入理解ART虚拟机—虚拟机的启动》
四,Java的运行时的功能简单来说分为:类的加载和函数Method的执行
参考文章《深入理解Dalvik虚拟机- 解释器的运行机制》
art的JNINativeInterface的定义如下:
const JNINativeInterface gJniNativeInterface = {
nullptr, // reserved0.
nullptr, // reserved1.
nullptr, // reserved2.
nullptr, // reserved3.
JNI::GetVersion,
JNI::DefineClass,
JNI::FindClass,
JNI::FromReflectedMethod,
JNI::FromReflectedField,
JNI::ToReflectedMethod,
JNI::GetSuperclass,
JNI::IsAssignableFrom,
JNI::ToReflectedField,
JNI::Throw,
JNI::ThrowNew,
JNI::ExceptionOccurred,
JNI::ExceptionDescribe,
JNI::ExceptionClear,
JNI::FatalError,
JNI::PushLocalFrame,
JNI::PopLocalFrame,
JNI::NewGlobalRef,
JNI::DeleteGlobalRef,
JNI::DeleteLocalRef,
JNI::IsSameObject,
JNI::NewLocalRef,
JNI::EnsureLocalCapacity,
JNI::AllocObject,
JNI::NewObject,
JNI::NewObjectV,
JNI::NewObjectA,
JNI::GetObjectClass,
JNI::IsInstanceOf,
JNI::GetMethodID,
JNI::CallObjectMethod,
JNI::CallObjectMethodV,
JNI::CallObjectMethodA,
JNI::CallBooleanMethod,
JNI::CallBooleanMethodV,
JNI::CallBooleanMethodA,
JNI::CallByteMethod,
JNI::CallByteMethodV,
JNI::CallByteMethodA,
JNI::CallCharMethod,
JNI::CallCharMethodV,
JNI::CallCharMethodA,
JNI::CallShortMethod,
JNI::CallShortMethodV,
JNI::CallShortMethodA,
JNI::CallIntMethod,
JNI::CallIntMethodV,
JNI::CallIntMethodA,
JNI::CallLongMethod,
JNI::CallLongMethodV,
JNI::CallLongMethodA,
JNI::CallFloatMethod,
JNI::CallFloatMethodV,
JNI::CallFloatMethodA,
JNI::CallDoubleMethod,
JNI::CallDoubleMethodV,
JNI::CallDoubleMethodA,
JNI::CallVoidMethod,
JNI::CallVoidMethodV,
JNI::CallVoidMethodA,
JNI::CallNonvirtualObjectMethod,
JNI::CallNonvirtualObjectMethodV,
JNI::CallNonvirtualObjectMethodA,
JNI::CallNonvirtualBooleanMethod,
JNI::CallNonvirtualBooleanMethodV,
JNI::CallNonvirtualBooleanMethodA,
JNI::CallNonvirtualByteMethod,
JNI::CallNonvirtualByteMethodV,
JNI::CallNonvirtualByteMethodA,
JNI::CallNonvirtualCharMethod,
JNI::CallNonvirtualCharMethodV,
JNI::CallNonvirtualCharMethodA,
JNI::CallNonvirtualShortMethod,
JNI::CallNonvirtualShortMethodV,
JNI::CallNonvirtualShortMethodA,
JNI::CallNonvirtualIntMethod,
JNI::CallNonvirtualIntMethodV,
JNI::CallNonvirtualIntMethodA,
JNI::CallNonvirtualLongMethod,
JNI::CallNonvirtualLongMethodV,
JNI::CallNonvirtualLongMethodA,
JNI::CallNonvirtualFloatMethod,
JNI::CallNonvirtualFloatMethodV,
JNI::CallNonvirtualFloatMethodA,
JNI::CallNonvirtualDoubleMethod,
JNI::CallNonvirtualDoubleMethodV,
JNI::CallNonvirtualDoubleMethodA,
JNI::CallNonvirtualVoidMethod,
JNI::CallNonvirtualVoidMethodV,
JNI::CallNonvirtualVoidMethodA,
JNI::GetFieldID,
JNI::GetObjectField,
JNI::GetBooleanField,
JNI::GetByteField,
JNI::GetCharField,
JNI::GetShortField,
JNI::GetIntField,
JNI::GetLongField,
JNI::GetFloatField,
JNI::GetDoubleField,
JNI::SetObjectField,
JNI::SetBooleanField,
JNI::SetByteField,
JNI::SetCharField,
JNI::SetShortField,
JNI::SetIntField,
JNI::SetLongField,
JNI::SetFloatField,
JNI::SetDoubleField,
JNI::GetStaticMethodID,
JNI::CallStaticObjectMethod,
JNI::CallStaticObjectMethodV,
JNI::CallStaticObjectMethodA,
JNI::CallStaticBooleanMethod,
JNI::CallStaticBooleanMethodV,
JNI::CallStaticBooleanMethodA,
JNI::CallStaticByteMethod,
JNI::CallStaticByteMethodV,
JNI::CallStaticByteMethodA,
JNI::CallStaticCharMethod,
JNI::CallStaticCharMethodV,
JNI::CallStaticCharMethodA,
JNI::CallStaticShortMethod,
JNI::CallStaticShortMethodV,
JNI::CallStaticShortMethodA,
JNI::CallStaticIntMethod,
JNI::CallStaticIntMethodV,
JNI::CallStaticIntMethodA,
JNI::CallStaticLongMethod,
JNI::CallStaticLongMethodV,
JNI::CallStaticLongMethodA,
JNI::CallStaticFloatMethod,
JNI::CallStaticFloatMethodV,
JNI::CallStaticFloatMethodA,
JNI::CallStaticDoubleMethod,
JNI::CallStaticDoubleMethodV,
JNI::CallStaticDoubleMethodA,
JNI::CallStaticVoidMethod,
JNI::CallStaticVoidMethodV,
JNI::CallStaticVoidMethodA,
JNI::GetStaticFieldID,
JNI::GetStaticObjectField,
JNI::GetStaticBooleanField,
JNI::GetStaticByteField,
JNI::GetStaticCharField,
JNI::GetStaticShortField,
JNI::GetStaticIntField,
JNI::GetStaticLongField,
JNI::GetStaticFloatField,
JNI::GetStaticDoubleField,
JNI::SetStaticObjectField,
JNI::SetStaticBooleanField,
JNI::SetStaticByteField,
JNI::SetStaticCharField,
JNI::SetStaticShortField,
JNI::SetStaticIntField,
JNI::SetStaticLongField,
JNI::SetStaticFloatField,
JNI::SetStaticDoubleField,
JNI::NewString,
JNI::GetStringLength,
JNI::GetStringChars,
JNI::ReleaseStringChars,
JNI::NewStringUTF,
JNI::GetStringUTFLength,
JNI::GetStringUTFChars,
JNI::ReleaseStringUTFChars,
JNI::GetArrayLength,
JNI::NewObjectArray,
JNI::GetObjectArrayElement,
JNI::SetObjectArrayElement,
JNI::NewBooleanArray,
JNI::NewByteArray,
JNI::NewCharArray,
JNI::NewShortArray,
JNI::NewIntArray,
JNI::NewLongArray,
JNI::NewFloatArray,
JNI::NewDoubleArray,
JNI::GetBooleanArrayElements,
JNI::GetByteArrayElements,
JNI::GetCharArrayElements,
JNI::GetShortArrayElements,
JNI::GetIntArrayElements,
JNI::GetLongArrayElements,
JNI::GetFloatArrayElements,
JNI::GetDoubleArrayElements,
JNI::ReleaseBooleanArrayElements,
JNI::ReleaseByteArrayElements,
JNI::ReleaseCharArrayElements,
JNI::ReleaseShortArrayElements,
JNI::ReleaseIntArrayElements,
JNI::ReleaseLongArrayElements,
JNI::ReleaseFloatArrayElements,
JNI::ReleaseDoubleArrayElements,
JNI::GetBooleanArrayRegion,
JNI::GetByteArrayRegion,
JNI::GetCharArrayRegion,
JNI::GetShortArrayRegion,
JNI::GetIntArrayRegion,
JNI::GetLongArrayRegion,
JNI::GetFloatArrayRegion,
JNI::GetDoubleArrayRegion,
JNI::SetBooleanArrayRegion,
JNI::SetByteArrayRegion,
JNI::SetCharArrayRegion,
JNI::SetShortArrayRegion,
JNI::SetIntArrayRegion,
JNI::SetLongArrayRegion,
JNI::SetFloatArrayRegion,
JNI::SetDoubleArrayRegion,
JNI::RegisterNatives,
JNI::UnregisterNatives,
JNI::MonitorEnter,
JNI::MonitorExit,
JNI::GetJavaVM,
JNI::GetStringRegion,
JNI::GetStringUTFRegion,
JNI::GetPrimitiveArrayCritical,
JNI::ReleasePrimitiveArrayCritical,
JNI::GetStringCritical,
JNI::ReleaseStringCritical,
JNI::NewWeakGlobalRef,
JNI::DeleteWeakGlobalRef,
JNI::ExceptionCheck,
JNI::NewDirectByteBuffer,
JNI::GetDirectBufferAddress,
JNI::GetDirectBufferCapacity,
JNI::GetObjectRefType,
};
这些函数的定义在jni_internal.cc。
我们要分析art的运行机制,就需要弄清楚类的加载和art函数的执行:
一,类的加载
dalvik的类加载我们已经在《深入理解Dalvik虚拟机- Android应用进程启动过程分析》分析了,Android应用进程启动的时候会创建BaseDexClassLoader,这个BaseDexClassLoader包含了自身apk。再回顾一下过程:
1, app_process作为zygote server通过local socket处理进程创建请求,zygote server是在ZygoteInit.main函数里调用ZygoteInit.runSelectLoop监听。
2, 接收到zygote client的fork请求之后,调用ZygoteConnection.runOnce,调用Zygote.forkAndSpecialize创建新进程。
3, 进程创建之后,由ZygoteConnection.handleParentProc来初始化进程,最终会调用ActivityThread.main函数。
4, ActivityThread.main -> ActivityThread.attach -> ActivityThread.bindApplication -> Activity.handleBindApplication,handleBindApplication会初始化BaseDexClassLoader。
5, 类的加载经过了ClassLoader.loadClass->BaseDexClassLoader.findClass->DexPathList.findClass->DexFile.loadClassBinaryName->DexFile.defineClassNative->DexFile_defineClassNative(runtime/native/dalvik_system_DexFile.cc)
这个初始化过程,art和dalvik都是一样的。art的DexFile_defineClassNative由ClassLinker的DefineClass来加载类。
static jclass DexFile_defineClassNative(JNIEnv* env, jclass,jstring javaName, jobject javaLoader, jobject cookie){
std::unique_ptr> dex_files = ConvertJavaArrayToNative(env, cookie);
if (dex_files.get() ==nullptr) {
VLOG(class_linker)<< "Failed to find dex_file";
DCHECK(env->ExceptionCheck());
return nullptr;
}
ScopedUtfChars class_name(env, javaName);
if (class_name.c_str()== nullptr) {
VLOG(class_linker)<< "Failed to find class_name";
return nullptr;
}
const std::stringdescriptor(DotToDescriptor(class_name.c_str()));
const size_thash(ComputeModifiedUtf8Hash(descriptor.c_str()));
for (auto& dex_file: *dex_files) {
constDexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def !=nullptr) {
ScopedObjectAccesssoa(env);
ClassLinker*class_linker = Runtime::Current()->GetClassLinker();
class_linker->RegisterDexFile(*dex_file);
StackHandleScope<1> hs(soa.Self());
Handleclass_loader(hs.NewHandle(soa.Decode(javaLoader)));
mirror::Class*result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash, class_loader, *dex_file, *dex_class_def);
if (result !=nullptr) {
VLOG(class_linker)<< "DexFile_defineClassNative returning " << result << " for " << class_name.c_str();
return soa.AddLocalReference(result);
}
}
}
VLOG(class_linker)<< "Failed to find dex_class_def " <
return nullptr;
}
类的加载除了创建Class只外,还有加载类的字段和方法,这个由ClassLinker::LoadClass来完成。
void ClassLinker::LoadClass(Thread* self, const DexFile&dex_file, const DexFile::ClassDef& dex_class_def, Handle klass) {
const uint8_t*class_data = dex_file.GetClassData(dex_class_def);
if (class_data ==nullptr) {
return; // no fields or methods - for example a markerinterface
}
bool has_oat_class = false;
if(Runtime::Current()->IsStarted() &&!Runtime::Current()->IsAotCompiler()) {
OatFile::OatClassoat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class);
if (has_oat_class){
LoadClassMembers(self, dex_file, class_data, klass,&oat_class);
}
}
if (!has_oat_class){
LoadClassMembers(self, dex_file, class_data, klass, nullptr);
}
}
二,函数的执行
一旦类的加载完成,那么就可以调用类的成员函数了,之前的解释器运行机制那篇文章介绍过,Java的执行是以Method为执行单元的,所以我们分析art的运行机制,其实就是分析Method的运行机制。
《深入理解Dalvik虚拟机- Android应用进程启动过程分析》可知,ActivityThread是进程在启动的时候传类名,在进程启动之后,由handleParentProc执行main函数,因此第一个被执行的java函数是ActivityThread.main。
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, null);
ActivityThread.main是最终由AndroidRuntime::callMain执行
status_tAndroidRuntime::callMain(const String8& className, jclass clazz, const Vector& args)
{
JNIEnv* env;
jmethodID methodId;
ALOGD("Calling main entry %s",className.string());
env = getJNIEnv();
if (clazz == NULL || env == NULL) {
return UNKNOWN_ERROR;
}
methodId = env->GetStaticMethodID(clazz,"main", "([Ljava/lang/String;)V");
if (methodId == NULL) {
ALOGE("ERROR: could not findmethod %s.main(String[])n", className.string());
return UNKNOWN_ERROR;
}
/*
* We want to call main() with a Stringarray with our arguments in it.
* Create an array and populate it.
*/
jclass stringClass;
jobjectArray strArray;
const size_t numArgs = args.size();
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(numArgs,stringClass, NULL);
for (size_t i = 0; i < numArgs; i ){
jstring argStr = env->NewStringUTF(args[i].string());
env->SetObjectArrayElement(strArray,i, argStr);
}
env->CallStaticVoidMethod(clazz,methodId, strArray);
return NO_ERROR;
}
实际会调用JNINativeInterface的CallStaticVoidMethod,上面已经介绍过,该函数的定义在runtime/jni_internal.cc里:
static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap,mid);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccesssoa(env);
InvokeWithVarArgs(soa, nullptr, mid, ap);
va_end(ap);
}
InvokeWithVarArgs是执行函数的入口,定义在runtime/reflection.cc,最终是调用了ArtMethod::Invoke。
JValue InvokeWithVarArgs(constScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// We want to make surethat the stack is not within a small distance from the
// protected region incase we are calling into a leaf function whose stack
// check has beenelided.
if(UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
ThrowStackOverflowError(soa.Self());
return JValue();
}
ArtMethod* method = soa.DecodeMethod(mid);
bool is_string_init = method->GetDeclaringClass()->IsStringClass() &&method->IsConstructor();
if (is_string_init){
// Replace calls toString. with equivalent StringFactory call.
method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
}
mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode(obj);
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue result;
ArgArrayarg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
if (is_string_init){
// For string init,remap original receiver to StringFactory result.
UpdateReference(soa.Self(), obj, result.GetL());
}
return result;
}
static void InvokeWithArgArray(constScopedObjectAccessAlreadyRunnable& soa, ArtMethod* method, ArgArray* arg_array, JValue* result, const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
if (UNLIKELY(soa.Env()->check_jni)){
CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(sizeof(void*)), args);
}
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result,shorty);
}
我们知道ART的运行模式是AOT的,在apk安装的时候,每个DexMethod都会由dex2oat编译成目标代码,而不再是虚拟机执行的字节码,但同时Dex字节码仍然还在OAT里存在,所以ART的代码执行既支持QuickCompiledCode模式,也同时支持解释器模式以及JIT执行模式。看ArtMethod::Invoke
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*result, const char* shorty){
if (UNLIKELY(__builtin_frame_address(0) GetStackEnd())) {
ThrowStackOverflowError(self);
return;
}
if (kIsDebugBuild) {
self->AssertThreadSuspensionIsAllowable();
CHECK_EQ(kRunnable, self->GetState());
CHECK_STREQ(GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(), 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.
if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) {
if (IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result);
} else {
mirror::Object* receiver = reinterpret_cast*>(&args[0])->AsMirrorPtr();
art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args 1, result);
}
} else {
DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(),sizeof(void*));
constexpr bool kLogInvocationStartAndReturn = false;
bool have_quick_code = GetEntryPointFromQuickCompiledCode()!= nullptr;
if (LIKELY(have_quick_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf( "Invoking '%s' quick code=%pstatic=%d", PrettyMethod(this).c_str(), GetEntryPointFromQuickCompiledCode(), static_cast(IsStatic() ? 1 : 0));
}
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild &&runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
DCHECK(!runtime->UseJit());
CHECK(IsEntrypointInterpreter()) << "Don't call compiled code when -Xint " << PrettyMethod(this);
}
#if defined(__LP64__) || defined(__arm__) || defined(__i386__)
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
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
#endif
if (UNLIKELY(self->GetException() ==Thread::GetDeoptimizationException())) {
// Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
self->ClearException();
ShadowFrame* shadow_frame = self->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
result->SetJ(self->PopDeoptimizationReturnValue().GetJ());
self->SetTopOfStack(nullptr);
self->SetTopOfShadowStack(shadow_frame);
interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
}
if (kLogInvocationStartAndReturn) {
LOG(INFO) < GetEntryPointFromQuickCompiledCode());
}
} else {
LOG(INFO) << "Not invoking '"<< PrettyMethod(this) << "' code=null";
if (result != nullptr) {
result->SetJ(0);
}
}
}
// Pop transition.
self->PopManagedStackFragment(fragment);
}
Invoke可以进入OAT,Interpreter模式执行Method,如果当前是Interpreter模式,就调用art::interpreter::EnterInterpreterFromInvoke,如果是OAT模式,就调用art_quick_invoke_stub/art_quick_invoke_static_stub。
EnterInterpreterFromInvoke函数里会判断是native还是解释器执行:
voidEnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object*receiver, uint32_t* args,JValue* result) {
DCHECK_EQ(self, Thread::Current());
bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
if (UNLIKELY(__builtin_frame_address(0) GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
const DexFile::CodeItem* code_item = method->GetCodeItem();
uint16_t num_regs;
uint16_t num_ins;
if (code_item != nullptr) {
num_regs = code_item->registers_size_;
num_ins = code_item->ins_size_;
} else if (method->IsAbstract()) {
self->EndAssertNoThreadSuspension(old_cause);
ThrowAbstractMethodError(method);
return;
} else {
DCHECK(method->IsNative());
num_regs = num_ins =ArtMethod::NumArgRegisters(method->GetShorty());
if (!method->IsStatic()) {
num_regs ;
num_ins ;
}
}
// Set up shadow frame with matching number of reference slots to vregs.
ShadowFrame* last_shadow_frame = self->GetManagedStack()->GetTopShadowFrame();
void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
ShadowFrame*shadow_frame(ShadowFrame::Create(num_regs, last_shadow_frame, method, 0, memory));
self->PushShadowFrame(shadow_frame);
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
CHECK(receiver != nullptr);
shadow_frame->SetVRegReference(cur_reg, receiver);
cur_reg;
}
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
for (size_t shorty_pos = 0, arg_pos = 0; cur_reg< num_regs; shorty_pos, arg_pos, cur_reg ) {
DCHECK_LT(shorty_pos 1, shorty_len);
switch (shorty[shorty_pos 1]) {
case 'L': {
Object* o = reinterpret_cast*>(&args[arg_pos])->AsMirrorPtr();
shadow_frame->SetVRegReference(cur_reg, o);
break;
}
case 'J':
case 'D': {
uint64_t wide_value = (static_cast(args[arg_pos 1]) << 32) |args[arg_pos];
shadow_frame->SetVRegLong(cur_reg,wide_value);
cur_reg ;
arg_pos ;
break;
}
default:
shadow_frame->SetVReg(cur_reg,args[arg_pos]);
break;
}
}
self->EndAssertNoThreadSuspension(old_cause);
// Do this after populating the shadow frame in case EnsureInitialized causes a GC.
if (method->IsStatic() &&UNLIKELY(!method->GetDeclaringClass()->IsInitialized())) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self);
Handleh_class(hs.NewHandle(method->GetDeclaringClass()));
if(UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))){
CHECK(self->IsExceptionPending());
self->PopShadowFrame();
return;
}
}
if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, code_item,*shadow_frame, JValue());
if (result != nullptr) {
*result = r;
}
} else {
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
// Update args to be the args in the shadow frame since the input ones could hold stale
// references pointers due to movingGC.
args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
if (!Runtime::Current()->IsStarted()){
UnstartedRuntime::Jni(self, method, receiver, args, result);
} else {
InterpreterJni(self, method, shorty, receiver, args, result);
}
}
self->PopShadowFrame();
}
这个函数前面部分都在做参数压栈操作,最后几行进入主题,如果不是Native,那么调用Execute执行;Native函数则调用InterpreterJni。Execute就是art的解释器代码,Dex的字节码是通过ArtMethod::GetCodeItem函数获得,由Execute逐条执行。InterpreterJni通过GetEntryPointFromJni来获得native的函数,并执行。
if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, code_item, *shadow_frame, JValue());
if (result != nullptr){
*result = r;
}
} else {
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub)except during testing and image writing.
// Update args to bethe args in the shadow frame since the input ones could hold stale
// references pointersdue to moving GC.
args =shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
if(!Runtime::Current()->IsStarted()) {
UnstartedRuntime::Jni(self, method, receiver, args, result);
} else {
InterpreterJni(self, method, shorty, receiver, args, result);
}
}
再回调OAT的模式,art_quick_invoke_stub/art_quick_invoke_static_stub最终会调用到art_quick_invoke_stub_internal(arch/arm/quick_entrypoints_arm.S)
art_quick_invoke_stub_internal(arch/arm/quick_entrypoints_arm.S)
ENTRYart_quick_invoke_stub_internal
push {r4, r5, r6, r7, r8, r9, r10, r11, lr} @ spill regs
.cfi_adjust_cfa_offset 16
.cfi_rel_offset r4, 0
.cfi_rel_offset r5, 4
.cfi_rel_offset r6, 8
.cfi_rel_offset r7, 12
.cfi_rel_offset r8, 16
.cfi_rel_offset r9, 20
.cfi_rel_offset r10, 24
.cfi_rel_offset r11, 28
.cfi_rel_offset lr, 32
mov r11, sp @save the stack pointer
.cfi_def_cfa_register r11
mov r9, r3 @move managed thread pointer into r9
add r4, r2, #4 @create space for method pointer in frame
sub r4, sp, r4 @reserve & align *stack* to 16 bytes: native calling
and r4, #0xFFFFFFF0 @convention only aligns to 8B, so we have to ensure ART
mov sp, r4 @ 16B alignment ourselves.
mov r4, r0 @save method*
add r0, sp, #4 @pass stack pointer method ptr as dest for memcpy
bl memcpy @memcpy (dest, src, bytes)
mov ip, #0 @set ip to 0
str ip, [sp] @store null for method* at bottom of frame
ldr ip, [r11, #48] @load fp register argument array pointer
vldm ip, {s0-s15} @copy s0 - s15
ldr ip, [r11, #44] @load core register argument array pointer
mov r0, r4 @restore method*
add ip, ip, #4 @skip r0
ldm ip, {r1-r3} @copy r1 - r3
#ifdefARM_R4_SUSPEND_FLAG
mov r4, #SUSPEND_CHECK_INTERVAL @reset r4 to suspend check interval
#endif
ldr ip, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32] @ get pointer to the code
blx ip @call the method
mov sp, r11 @restore the stack pointer
.cfi_def_cfa_register sp
ldr r4, [sp, #40] @load result_is_float
ldr r9, [sp, #36] @load the result pointer
cmp r4, #0
ite eq
strdeq r0, [r9] @ store r0/r1 intoresult pointer
vstrne d0, [r9] @ store s0-s1/d0 intoresult pointer
pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} @ restore spill regs
ENDart_quick_invoke_stub_internal
找到ArtMethod的entry_point_from_quick_compiled_code_字段,这个就是EntryPointFromQuickCompiledCode,从而进入OAT函数执行。
#define ART_METHOD_QUICK_CODE_OFFSET_32 36
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32, art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
EntryPointFromQuickCompiledCode的初始化在class_linker的LoadClassMembers时调用的LinkCode,有下面几种类型:
1,SetEntryPointFromQuickCompiledCode(GetQuickCode());// 这个是执行OatMethod
2,SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); // Dex Method
3,SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());// Native Method
4,SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); // method->IsStatic() && !method->IsConstructor()
如果是强制使用了解释器模式,那么执行的是代码GetQuickToInterpreterBridge(non-static,
non-native)或GetQuickGenericJniStub(non-static, native)或GetQuickResolutionStub(static),这几个EntryPoint对应的实际执行函数如下。
GetQuickGenericJniStub — artQuickGenericJniTrampoline
GetQuickResolutionStub — artQuickResolutionTrampoline
GetQuickToInterpreterBridge — artQuickToInterpreterBridge
ArtMthod被Resolve之后,如果是走Oat模式就会执行GetQuickCode。
楼上是EntryPointFromQuickCompiledCode的情况:
不同的执行模式有不同的EntryPoint:
1,解释器-EntryPointFromInterpreter
在interpreter/interpreter_common.cc里会在执行解释器函数时,会获得ArtMethod的Interpret EntryPoint执行;
2,Jni - EntryPointFromJni
interpreter/interpreter.cc,InterpreterJni函数会获得ArtMethod的Jni EntryPoint执行;
3,Oat- EntryPointFromQuickCompiledCode
DexCache在Init的时候会将Method都初始化为ResolutionMethod,这个Resolution Method是没有dex method id的,是个RuntimeMethod,这是lazy load method,运行时resolve之后才会替换成实际的ArtMethod。
void DexCache::Init(const DexFile* dex_file, String*location, ObjectArray* strings, ObjectArray* resolved_types, PointerArray*resolved_methods, PointerArray* resolved_fields, size_t pointer_size) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
CHECK(strings != nullptr);
CHECK(resolved_types != nullptr);
CHECK(resolved_methods != nullptr);
CHECK(resolved_fields != nullptr);
SetDexFile(dex_file);
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
SetFieldObject(StringsOffset(), strings);
SetFieldObject(ResolvedFieldsOffset(),resolved_fields);
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);
SetFieldObject(ResolvedMethodsOffset(), resolved_methods);
Runtime* construntime = Runtime::Current();
if(runtime->HasResolutionMethod()) {
// Initialize the resolve methods array to contain trampolines for resolution.
Fixup(runtime->GetResolutionMethod(), pointer_size);
}
}
void DexCache::Fixup(ArtMethod* trampoline, size_tpointer_size) {
// Fix up the resolve methods array to contain trampoline for resolution.
CHECK(trampoline !=nullptr);
CHECK(trampoline->IsRuntimeMethod());
auto*resolved_methods = GetResolvedMethods();
for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i ) {
if(resolved_methods->GetElementPtrSize(i, pointer_size) ==nullptr) {
resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);
}
}
}
resolution method的EntryPointFromQuickCompiledCode指向GetQuickResolutionStub,意思就是一开始,这些函数的执行点都是从artQuickResolutionTrampoline开始。
//Lazily resolve a method for quick. Called by stub code.
extern"C" const void* artQuickResolutionTrampoline(ArtMethod* called, mirror::Object*receiver, Thread* self, ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
// Start new JNI local reference state
JNIEnvExt* env = self->GetJniEnv();
ScopedObjectAccessUnchecked soa(env);
ScopedJniEnvLocalRefState env_state(env);
const char* old_cause = self->StartAssertNoThreadSuspension("Quick method resolution setup");
// Compute details about the called method(avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
MethodReference called_method(nullptr, 0);
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
if (!called_method_known_on_entry) {
uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
const DexFile::CodeItem* code;
called_method.dex_file = caller->GetDexFile();
code = caller->GetCodeItem();
CHECK_LT(dex_pc,code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
bool is_range;
switch (instr_code) {
case Instruction::INVOKE_DIRECT:
invoke_type = kDirect;
is_range = false;
break;
case Instruction::INVOKE_DIRECT_RANGE:
invoke_type = kDirect;
is_range = true;
break;
case Instruction::INVOKE_STATIC:
invoke_type = kStatic;
is_range = false;
break;
case Instruction::INVOKE_STATIC_RANGE:
invoke_type = kStatic;
is_range = true;
break;
case Instruction::INVOKE_SUPER:
invoke_type = kSuper;
is_range = false;
break;
case Instruction::INVOKE_SUPER_RANGE:
invoke_type = kSuper;
is_range = true;
break;
case Instruction::INVOKE_VIRTUAL:
invoke_type = kVirtual;
is_range = false;
break;
case Instruction::INVOKE_VIRTUAL_RANGE:
invoke_type = kVirtual;
is_range = true;
break;
case Instruction::INVOKE_INTERFACE:
invoke_type = kInterface;
is_range = false;
break;
case Instruction::INVOKE_INTERFACE_RANGE:
invoke_type = kInterface;
is_range = true;
break;
default:
LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
UNREACHABLE();
}
called_method.dex_method_index = (is_range)? instr->VRegB_3rc() : instr->VRegB_35c();
} else {
invoke_type = kStatic;
called_method.dex_file = called->GetDexFile();
called_method.dex_method_index =called->GetDexMethodIndex();
}
uint32_t shorty_len;
const char* shorty = called_method.dex_file->GetMethodShorty(called_method.dex_file->GetMethodId(called_method.dex_method_index), &shorty_len);
RememberForGcArgumentVisitor visitor(sp,invoke_type == kStatic, shorty, shorty_len, &soa);
visitor.VisitArguments();
self->EndAssertNoThreadSuspension(old_cause);
const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;
// Resolve method filling in dex cache.
if (!called_method_known_on_entry) {
StackHandleScope<1> hs(self);
mirror::Object* dummy = nullptr;
HandleWrapperh_receiver(hs.NewHandleWrapper(virtual_or_interface? &receiver : &dummy));
DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);
called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);
}
const void* code = nullptr;
if (LIKELY(!self->IsExceptionPending())){
// Incompatible class change should have been handled in resolve method.
CHECK(!called->CheckIncompatibleClassChange(invoke_type)) << PrettyMethod(called) <<" " << invoke_type;
if (virtual_or_interface) {
// Refine called method based on receiver.
CHECK(receiver != nullptr) << invoke_type
ArtMethod* orig_called = called;
if (invoke_type == kVirtual) {
called =receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));
} else {
called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));
}
CHECK(called != nullptr) << PrettyMethod(orig_called) << " " << PrettyTypeOf(receiver) << " "
<< invoke_type << " " << orig_called->GetVtableIndex();
// We came here because of sharpening.Ensure the dex cache is up-to-date on the method index
// of the sharpened method avoiding dirtying the dex cache if possible.
// Note, called_method.dex_method_index references the dex method before the
// FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
// about the name and signature.
uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
if(!called->HasSameDexCacheResolvedMethods(caller)) {
// Calling from one dex file to another, need to compute the method index appropriate to
// the caller's dex file. Since we get here only if the original called was a runtime
// method, we've got the correct dex_file and a dex_method_idx from above.
DCHECK(!called_method_known_on_entry);
DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);
const DexFile* caller_dex_file = called_method.dex_file;
uint32_tcaller_method_name_and_sig_index = called_method.dex_method_index;
update_dex_cache_method_index = called->FindDexMethodIndexInOtherDexFile(*caller_dex_file, caller_method_name_and_sig_index);
}
if ((update_dex_cache_method_index !=DexFile::kDexNoIndex) && (caller->GetDexCacheResolvedMethod(update_dex_cache_method_index,sizeof(void*)) != called)) {
caller->SetDexCacheResolvedMethod(update_dex_cache_method_index,called, sizeof(void*));
}
} else if (invoke_type == kStatic) {
const auto called_dex_method_idx = called->GetDexMethodIndex();
// For static invokes, we may dispatch to the static method in the superclass but resolve
// using the subclass. To prevent getting slow paths on each invoke, we force set the
// resolved method for the super class dex method index if we are in the same dex file.
// b/19175856
if (called->GetDexFile() == called_method.dex_file&& called_method.dex_method_index !=called_dex_method_idx) {
called->GetDexCache()->SetResolvedMethod(called_dex_method_idx,called, sizeof(void*));
}
}
// Ensure that the called method's class is initialized.
StackHandleScope<1>hs(soa.Self());
Handlecalled_class(hs.NewHandle(called->GetDeclaringClass()));
linker->EnsureInitialized(soa.Self(), called_class, true, true);
if (LIKELY(called_class->IsInitialized())){
if(UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {
// If we are single-stepping or the called method is deoptimized (by a
// breakpoint, for example), then we have to execute the called method
// with the interpreter.
code =GetQuickToInterpreterBridge();
} else if(UNLIKELY(Dbg::IsForcedInstrumentationNeededForResolution(self, caller))){
// If the caller is deoptimized (by a breakpoint, for example), we have to
// continue its execution with interpreter when returning from the called
// method. Because we do not want to execute the called method with the
// interpreter, we wrap its execution into the instrumentation stubs.
// When the called method returns, it will execute the instrumentation
// exit hook that will determine the need of the interpreter with a call
// toDbg::IsForcedInterpreterNeededForUpcall and deoptimize the stack if
// it is needed.
code =GetQuickInstrumentationEntryPoint();
} else {
code =called->GetEntryPointFromQuickCompiledCode();
}
} else if(called_class->IsInitializing()) {
if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self,called))) {
// If we are single-stepping or the called method is deoptimized (by a
// breakpoint, for example), then we have to execute the called method
// with the interpreter.
code =GetQuickToInterpreterBridge();
} else if (invoke_type == kStatic) {
// Class is still initializing, go too at and grab code (trampoline must be left in place
// until class is initialized to stop races between threads).
code = linker->GetQuickOatCodeFor(called);
} else {
// No trampoline for non-static methods.
code = called->GetEntryPointFromQuickCompiledCode();
}
} else {
DCHECK(called_class->IsErroneous());
}
}
CHECK_EQ(code == nullptr,self->IsExceptionPending());
// Fixup any locally saved objects may havemoved during a GC.
visitor.FixupReferences();
// Place called method in callee-save frameto be placed as first argument to quick method.
*sp = called;
return code;
}
上面代码可知,找到当前ArtMethod的流程大致的逻辑就是,根据caller函数ArtMethod的dex代码,可以找到这个ArtMethod的函数调用类型(INVOKE_DIRECT,INVOKE_STATIC,INVOKE_SUPER,INVOKE_VIRTUAL etc.),不同的类型查找的方式不一样,比如Virtual Method要从虚表里找,Super Method要从父类的Method里去找,找到之后调用ClassLinker的ResolveMethod来解析,解析出来的ArtMethod的就是上面LinkCode过的ArtMethod。
下面就是ResolveMethod函数的实现,Class查找到Method,之后在赋值到DexCache里,这样下次再执行就能直接找到Resolved Method。
ArtMethod*ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_tmethod_idx,
Handle dex_cache,
Handle class_loader,
ArtMethod* referrer, InvokeType type) {
DCHECK(dex_cache.Get() != nullptr);
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
if (resolved != nullptr &&!resolved->IsRuntimeMethod()) {
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) <GetDexMethodIndex();
return resolved;
}
// Fail, get the declaring class.
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file,method_id.class_idx_, dex_cache, class_loader);
if (klass == nullptr) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
// Scan using method_idx, this saves string compares but will only hit for matching dex
// caches/files.
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx,image_pointer_size_);
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
default:
LOG(FATAL) << "Unreachable -invocation type: " << type;
UNREACHABLE();
}
if (resolved == nullptr) {
// Search by name, which works across dex files.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
break;
}
}
// If we found a method, check for incompatible class changes.
if (LIKELY(resolved != nullptr &&!resolved->CheckIncompatibleClassChange(type))) {
// Be a good citizen and update the dex cache to speed subsequent calls.
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
return resolved;
} else {
// If we had a method, it's anincompatible-class-change error.
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
} else {
// We failed to find the method which means either an access error, an incompatible class
// change, or no such method. First try to find the method among direct and virtual methods.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect:
case kStatic:
resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
// Note: kDirect and kStatic are also mutually exclusive, but in that case we would
// have had a resolved method before, which triggers the "true" branch above.
break;
case kInterface:
case kVirtual:
case kSuper:
resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);
break;
}
// If we found something, check that it can be accessed by the referrer.
bool exception_generated = false;
if (resolved != nullptr &&referrer != nullptr) {
mirror::Class* methods_class =resolved->GetDeclaringClass();
mirror::Class* referring_class =referrer->GetDeclaringClass();
if(!referring_class->CanAccess(methods_class)) {
ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, resolved, type);
exception_generated = true;
} else if(!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) {
ThrowIllegalAccessErrorMethod(referring_class, resolved);
exception_generated = true;
}
}
if (!exception_generated) {
// Otherwise, throw anIncompatibleClassChangeError if we found something, and check
// interface methods and throw if we find the method there. If we find nothing, throw a
// NoSuchMethodError.
switch (type) {
case kDirect:
case kStatic:
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);
} else {
resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
} else {
ThrowNoSuchMethodError(type, klass, name, signature);
}
}
break;
case kInterface:
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
} else {
resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);
} else {
ThrowNoSuchMethodError(type, klass, name, signature);
}
}
break;
case kSuper:
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
} else {
ThrowNoSuchMethodError(type, klass, name, signature);
}
break;
case kVirtual:
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
} else {
resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
} else {
ThrowNoSuchMethodError(type, klass, name, signature);
}
}
break;
}
}
}
Thread::Current()->AssertPendingException();
return nullptr;
}
}
至此,Art Method的执行机制就算介绍完了,我们对整个函数执行机制都有个全局的概念了,包括:
1,Art怎么进入第一个Method;
2,ClassLinker在初始化的时候怎么加载成员函数(初始化几个EntryPoint);
3,DexCache初始化的时候将ArtMethod初始化成Resolution Method,后续在运行时ResolveMethod;
4,解释器模式在Art下是如何运行的;
1.4 Dalvik虚拟机运行机制
深入理解Dalvik虚拟机-解释器的运行机制
http://blog.csdn.net/threepigs/article/details/51061926
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:
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, "", "()V");
if(method == NULL) {
LOGVV("No found for %s", clazz->descriptor);
} else{
LOGVV("Invoking %s.", clazz->descriptor);
JValue unused;
dvmCallMethod(self, method, NULL, &unused);
}
...
}
从代码可见,类初始化的主要代码逻辑包括:
1、类对象加锁,所以类的加载是单线程的;
2、初始化static成员(initSFields);
3、调用,静态初始化块;
类的初始化块代码在的成员函数里。可见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, constMethod* 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, constMethod* 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, constMethod* 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(int i, intj){
int k = i j;
return k;
}
public static void main(String[] args) {
System.out.print(new HelloWorld().foo(1, 2));
}
}
Dalvik虚拟机有两个栈,一个Java栈,一个是VM的native栈,vm的栈是OS的函数调用栈,Java的栈则是由VM管理的栈,每次在dvmCallMethod的时候,在Method执行之前,会调用dvmPushInterpFrame(java→java)或者dvmPushJNIFrame(java→native),JNI的Frame比InterpFrame少了局部变量的栈空间,native函数的局部变量是在vm的native栈里,由OS负责压栈出栈。DvmCallMethod结束的时候会调用dvmPopFrame做Java Stack的出栈。
所以Java Method的执行就是dvmInterpret函数对这个Method的字节码做解析,函数的实参与局部变量都在Java的Stack里获取。SaveBlock是StackSaveArea数据结构,里面包含了当前函数对应的栈信息,包括返回地址等。而Native Method的执行就是Method的nativeFunc的执行,实参和局部变量都是在VM的native stack里。
Method的nativeFunc是native函数的入口,dalvik虚拟机上的java的函数hook技术,都是通过改变Method的属性,SET_METHOD_FLAG(method, ACC_NATIVE),伪装成native函数,再设置nativeFunc作为钩子函数,从而实现hook功能。很显然,hook了的method不再具有多态性。
nativeFunc的默认函数是dvmResolveNativeMethod(vm/Native.cpp):
void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread * self)
{
ClassObject* clazz = method->clazz;
/*
* If this is a static method, it could be called before the class
* has been initialized.
*/
if(dvmIsStaticMethod(method)) {
if(!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
assert(dvmCheckException(dvmThreadSelf()));
return;
}
} else{
assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
}
/* start with our internal-native methods */
DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
if(infunc != NULL) {
/* resolution always gets the same answer, so no race here */
IF_LOGVV() {
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
LOGVV(" resolved native %s.%s %s, invoking", clazz->descriptor, method->name, desc);
free(desc);
}
if(dvmIsSynchronizedMethod(method)) {
ALOGE("ERROR: internal-native can't be declared 'synchronized'");
ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
dvmAbort(); // harsh, but this is VM-internal problem
}
DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
dvmSetNativeFunc((Method*) method, dfunc, NULL);
dfunc(args, pResult, method, self);
return;
}
/* now scan any DLLs we have loaded for JNI signatures */
void* func = lookupSharedLibMethod(method);
if(func != NULL) {
/* found it, point it at the JNI bridge and then call it */
dvmUseJNIBridge((Method*) method, func);
(*method->nativeFunc)(args, pResult, method, self);
return;
}
IF_ALOGW() {
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
ALOGW("No implementation found for native %s.%s:%s", clazz->descriptor, method->name, desc);
free(desc);
}
dvmThrowUnsatisfiedLinkError("Native method not found", method);
}
dvmResolveNativeMethod首先会调用dvmLookupInternalNativeMethod查询这个函数是否预置的函数,主要是查下面的函数集:
static DalvikNativeClass gDvmNativeMethodSet[] = {
{ "Ljava/lang/Object;", dvm_java_lang_Object, 0},
{ "Ljava/lang/Class;", dvm_java_lang_Class, 0},
{ "Ljava/lang/Double;", dvm_java_lang_Double, 0},
{ "Ljava/lang/Float;", dvm_java_lang_Float, 0},
{ "Ljava/lang/Math;", dvm_java_lang_Math, 0},
{ "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0},
{ "Ljava/lang/String;", dvm_java_lang_String, 0},
{ "Ljava/lang/System;", dvm_java_lang_System, 0},
{ "Ljava/lang/Throwable;", dvm_java_lang_Throwable, 0},
{ "Ljava/lang/VMClassLoader;", dvm_java_lang_VMClassLoader, 0},
{ "Ljava/lang/VMThread;", dvm_java_lang_VMThread, 0},
{ "Ljava/lang/reflect/AccessibleObject;", dvm_java_lang_reflect_AccessibleObject, 0},
{ "Ljava/lang/reflect/Array;", dvm_java_lang_reflect_Array, 0},
{ "Ljava/lang/reflect/Constructor;", dvm_java_lang_reflect_Constructor, 0},
{ "Ljava/lang/reflect/Field;", dvm_java_lang_reflect_Field, 0},
{ "Ljava/lang/reflect/Method;", dvm_java_lang_reflect_Method, 0},
{ "Ljava/lang/reflect/Proxy;", dvm_java_lang_reflect_Proxy, 0},
{ "Ljava/util/concurrent/atomic/AtomicLong;", dvm_java_util_concurrent_atomic_AtomicLong, 0},
{ "Ldalvik/bytecode/OpcodeInfo;", dvm_dalvik_bytecode_OpcodeInfo, 0},
{ "Ldalvik/system/VMDebug;", dvm_dalvik_system_VMDebug, 0},
{ "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0},
{ "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0},
{ "Ldalvik/system/Zygote;", dvm_dalvik_system_Zygote, 0},
{ "Ldalvik/system/VMStack;", dvm_dalvik_system_VMStack, 0},
{ "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0},
{ "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",
dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0},
{ "Lorg/apache/harmony/dalvik/NativeTestTarget;",
dvm_org_apache_harmony_dalvik_NativeTestTarget, 0},
{ "Lsun/misc/Unsafe;", dvm_sun_misc_Unsafe, 0},
{ NULL, NULL, 0},
};
不是内置的话,就会加载so库,查询对应的native函数,查询的规则就是我们熟知的了,com.xx.Helloworld.foobar对应com_xx_Helloworld_foobar。要注意的是,这个函数并不是nativeFunc,接下来的dvmUseJNIBridge调用里,dvmCallJNIMethod会作为nativeFunc,这个函数主要需要将之前提到的java stack frame里的ins实参,转译成jni的函数调用参数。xposed/dexposed就会自己设置自己的nativeFun、自己接管native函数的执行。
dvmInterpret是解释器的代码入口,代码位置在interp/Interp.cpp
void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
{
InterpSaveState interpSaveState;
ExecutionSubModes savedSubModes;
. . .
interpSaveState = self->interpSave;
self->interpSave.prev = &interpSaveState;
. . .
self->interpSave.method = method;
self->interpSave.curFrame = (u4*) self->interpSave.curFrame;
self->interpSave.pc = method->insns;
. . .
typedef void(*Interpreter)(Thread*);
Interpreter stdInterp;
if(gDvm.executionMode == kExecutionModeInterpFast)
stdInterp = dvmMterpStd;
#if defined(WITH_JIT)
else if(gDvm.executionMode == kExecutionModeJit || gDvm.executionMode == kExecutionModeNcgO0 || gDvm.executionMode == kExecutionModeNcgO1)
stdInterp = dvmMterpStd;
#endif
else
stdInterp = dvmInterpretPortable;
// Call the interpreter
(*stdInterp)(self);
*pResult = self->interpSave.retval;
/* Restore interpreter state from previous activation */
self->interpSave = interpSaveState;
#if defined(WITH_JIT)
dvmJitCalleeRestore(calleeSave);
#endif
if(savedSubModes != kSubModeNormal) {
dvmEnableSubMode(self, savedSubModes);
}
}
Thread的一个很重要的field就是interpSave,是InterpSaveState类型的,里面包含了当前函数,pc,当前栈帧等重要的变量,dvmInterpret一开始调用的时候就会初始化。
Dalvik解释器有两个,一个是dvmInterpretPortable,一个是 dvmMterpStd。两者的区别在于,前者是从c 实现,后者是汇编实现。
dvmInterpretPortable是在vm/mterp/out/InterpC-portable.cpp中定义:
void dvmInterpretPortable(Thread* self)
{
. . .
DvmDex* methodClassDex; // curMethod->clazz->pDvmDex
JValue retval;
/* core state */
const Method* curMethod; // method we're interpreting
const u2* pc; // program counter
u4* fp; // frame pointer
u2 inst; // current instruction
/* instruction decoding */
u4 ref; // 16 or 32-bit quantity fetched directly
u2 vsrc1, vsrc2, vdst; // usually used for register indexes
/* method call setup */
const Method* methodToCall;
bool methodCallRange;
/* static computed goto table */
DEFINE_GOTO_TABLE(handlerTable);
/* copy state in */
curMethod = self->interpSave.method;
pc = self->interpSave.pc;
fp = self->interpSave.curFrame;
retval = self->interpSave.retval;
methodClassDex = curMethod->clazz->pDvmDex;
. . .
FINISH(0); /* fetch and execute first instruction */
/*--- start of opcodes ---*/
/* File: c/OP_NOP.cpp */
HANDLE_OPCODE(OP_NOP)
FINISH(1);
OP_END
/* File: c/OP_MOVE.cpp */
HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
vdst = INST_A(inst);
vsrc1 = INST_B(inst);
ILOGV("|move%s v%d,v%d %s(v%d=0xx)", (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1, kSpacing, vdst, GET_REGISTER(vsrc1));
SET_REGISTER(vdst, GET_REGISTER(vsrc1));
FINISH(1);
OP_END
…..
}
解释器的指令执行是通过跳转表来实现,DEFINE_GOTO_TABLE(handlerTable)定义了指令Op的goto表。FINISH(0),则表示从第一条指令开始执行。
# define FINISH(_offset) {
ADJUST_PC(_offset);
inst = FETCH(0);
if(self->interpBreak.ctl.subMode) {
dvmCheckBefore(pc, fp, self);
}
goto *handlerTable[INST_INST(inst)];
}
#define FETCH(_offset) (pc[(_offset)])
FETCH(0)获得当前要执行的指令,通过查跳转表handlerTable来跳转到这条指令的执行点,就是函数后面的HANDLE_OPCODE的定义。
后者是针对不同平台做过优化的解释器。dvmMterpStd会做汇编级的优化,dvmMterpStdRun的入口就是针对不同的平台指令集,有对应的解释器代码,比如armv7 neon对应的代码就在mterp/out/InterpAsm-armv7-a-neon.S。
dvmMterpStdRun:
#define MTERP_ENTRY1
.save {r4-r10,fp,lr};
stmfd sp!, {r4-r10,fp,lr} @ save 9regs
#define MTERP_ENTRY2
.pad #4;
sub sp, sp, #4 @ align 64
.fnstart
MTERP_ENTRY1
MTERP_ENTRY2
/* save stack pointer, add magic word for debuggerd */
str sp, [r0, #offThread_bailPtr] @ save SP for eventual return
/* set up "named" registers, figure out entry point */
mov rSELF, r0 @ set rSELF
LOAD_PC_FP_FROM_SELF() @ load rPC and rFP from "thread"
ldr rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
. . .
/* start executing the instruction at rPC */
FETCH_INST() @ load rINST from rPC
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
. . .
#define rPC r4
#define rFP r5
#define rSELF r6
#define rINST r7
#define rIBASE r8
非jit的情况下,先是FETCH_INST把pc的指令加载到rINST寄存器,之后GET_INST_OPCODE获得操作码and _reg, rINST, #255,是把rINST的低16位给ip寄存器,GOTO_OPCODE跳转到对应的地址。
#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
rIBASE 指向的curHandlerTable是跳转表的首地址,GOTO_OPCODE(ip)就将pc的地址指向该指令对应的操作码所在的跳转表地址。
static Thread* allocThread(intinterpStackSize)
#ifndef DVM_NO_ASM_INTERP
thread->mainHandlerTable = dvmAsmInstructionStart;
thread->altHandlerTable = dvmAsmAltInstructionStart;
thread->interpBreak.ctl.curHandlerTable = thread->mainHandlerTable;
#endif
可见dvmAsmInstructionStart就是跳转表的入口,定义在dvmMterpStdRun里,你可以在这里找到所有的Java字节码的指令对应的解释器代码。
比如new操作符对应的代码如下,先加载Thread.interpSave.methodClassDex,这是一个DvmDex指针,随后加载 DvmDex的pResClasses来查找类是否加载过,如果没加载过,那么跳转到 LOP_NEW_INSTANCE_resolve去加载类,如果加载过,就是类的初始化以及AllocObject的处理。LOP_NEW_INSTANCE_resolve就是调用clazz的dvmResolveClass加载。
/* ------------------------------ */
.balign 64
.L_OP_NEW_INSTANCE: /* 0x22 */
/* File: armv5te/OP_NEW_INSTANCE.S */
/*
* Create a new instance of a class.
*/
/* new-instance vAA, class@BBBB */
ldr r3, [rSELF, #offThread_methodClassDex] @ r3<- pDvmDex
FETCH(r1, 1) @ r1<- BBBB
ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
#if defined(WITH_JIT)
add r10, r3, r1, lsl #2@ r10<- &resolved_class
#endif
EXPORT_PC() @ req'd forinit, resolve, alloc
cmp r0, #0@ already resolved?
beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
.LOP_NEW_INSTANCE_resolved: @ r0=class
ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
cmp r1, #CLASS_INITIALIZED @ has classbeen initialized?
bne .LOP_NEW_INSTANCE_needinit @ no, init classnow
.LOP_NEW_INSTANCE_initialized: @ r0=class
mov r1, #ALLOC_DONT_TRACK @ flags foralloc call
bl dvmAllocObject @ r0<- newobject
b .LOP_NEW_INSTANCE_finish @ continue
.LOP_NEW_INSTANCE_needinit:
mov r9, r0 @ save r0
bl dvmInitClass @ initialize class
cmp r0, #0 @ check booleanresult
mov r0, r9 @ restore r0
bne .LOP_NEW_INSTANCE_initialized @ success, continue
b common_exceptionThrown @ failed, deal with init exception
/*
* Resolution required. This is the least-likely path.
*
* r1 holds BBBB
*/
.LOP_NEW_INSTANCE_resolve:
ldr r3, [rSELF, #offThread_method] @ r3<- self->method
mov r2, #0 @ r2<- false
ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
bl dvmResolveClass @ r0<- resolved ClassObject ptr
cmp r0, #0 @ got null?
bne .LOP_NEW_INSTANCE_resolved @ no, continue
b common_exceptionThrown @ yes, handle exception
2 参考链接
深入理解Dalvik虚拟机-解释器的运行机制
http://blog.csdn.net/threepigs/article/details/51061926
深入理解ART虚拟机—ART的函数运行机制
http://blog.csdn.net/threepigs/article/details/52884904
Dalvik虚拟机的运行过程分析
http://shangxun.iteye.com/blog/2124440
深入理解Dalvik虚拟机-Android应用进程启动过程分析
http://blog.csdn.net/threepigs/article/details/50779056
java virtual machine(java虚拟机的运行机制)?
http://webzixue.iteye.com/blog/1227802