Android(安卓)JVM的运行过程
JVM有三重意思:java virtual machine specification,java virtual machine implementation, java virtual machine runtime instance. 作为Android系统的使用者,我们主要关心runtime instance,不过为了对JVM全貌有个了解,还是需要简要回顾一下JVM specification描述的模型。
因为涉及体系结构,编译器和操作系统铁三角,这个话题还是很大。不过我们的目标是理解一个APK是如何在Android JVM上运行的,所以重点关注操作系统方面,编译器和体系结构部分且当作黑盒子看待。
JVM 模型
JVM是一个抽象的计算机,在它上面运行的程序是平台无关的。JVM通过Class loader来加载class并执行其中的bytecode。执行bytecode的execution engine根据具体的实现有很大差异。JVM 可以通过native method来与host platform交互。
JVM conceptual arch:
JVM上memory包含集中类型: method area, java heap, java stack, native heap, native stack:
Runtime data areas shared among all threads:
Runtime data areas exclusive to each threads:
JVM class loader:
ART JVM runtime implementation
How did ART VM come?
- Boot-loader loads kernel and start init process;
- Init start Zygote process;
- Zygote initializes a Dalvik/ART VM which preloads and pre-initializes core libraries classes;
- Zygote keeps in an idle state by system and waits for socket requests;
- Once an application execution request occur, Zygote forks itself and create new process with pre-loaded Darvik/ART VM;
Android平台的ClassLoader
java.lang.ClassLoader是所有ClassLoader的最终父类。ClassLoader中重要的方法是loadClass(String name),其他的子类都继承了此方法且没有进行复写。LoadClass采用parent delegation.
和java虚拟机中不同的是BootClassLoader是ClassLoader内部类,由java代码实现而不是c++实现,是Android平台上所有ClassLoader的最终parent,这个内部类是包内可见,所以我们没法使用。
URLClassLoader 只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。
PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,其中的主要逻辑都是在BaseDexClassLoader完成的。其实在BaseDexClassLoader里对”.jar”,”.zip”,”.apk”,”.dex”后缀的文件最后都会生成一个对应的dex文件,所以最终处理的还是dex文件。
DexClassLoader支持加载APK、DEX和JAR,也可以从SD卡进行加载。一般我们都是用这个DexClassLoader来作为动态加载的加载器。
PathClassLoader是用来加载Android系统类和应用的类,并且不建议开发者使用。
都继承自BaseDexClassLoader,只是有不同的构造函数,唯一的区别PathClassLoader就是optimizedDirectory参数为null,很好理解嘛,PathClassLoader加载的是data/app/…安装目录下的dex,但是,DexClassLoader加载外部未安装的dex/apk/jar/zip等,所以需要把最后的odex文件放在optimizedDirectory目录,所以不能为null
Android JVM的Class diagram
AndroidRuntime: 著名的类,不解释;其核心是java空间与native空间的互动。说道java与native的互动,就涉及两个数据结构:
JNIEnv: 对应JNI 表;对应主线程的执行的JNI 环境。
JavaVM: 对应Java callback表;一个进程之包含一个该结构,提供调用接口来创建和销毁虚拟机自己。
typedef const struct JNINativeInterface* JNIEnv;typedef const struct JNIInvokeInterface* JavaVM;
JavaVMExt:
JNIEnvExt:
struct JavaVMExt { const struct JNIInvokeInterface* funcTable; /* must be first */ const struct JNIInvokeInterface* baseFuncTable; /* head of list of JNIEnvs associated with this VM */ JNIEnvExt* envList; pthread_mutex_t envListLock;};
struct JNIEnvExt { const struct JNINativeInterface* funcTable; /* must be first */ const struct JNINativeInterface* baseFuncTable; u4 envThreadId; Thread* self; /* if nonzero, we are in a "critical" JNI call */ int critical; struct JNIEnvExt* prev; struct JNIEnvExt* next;};
Android JVM在Zygote中的启动过程
- 创建一个JavaJVM实例;
- 加载Java核心类及其JNI方法;
- 为主线程设置一个JNI 环境 JNIEnv;
- 注册Android核心类的JNI方法;
Android JVM的运行过程
- 对于Zygote进程,JVM以com.android.internal.os.ZygoteInit类的静态成员函数main为入口点执行,然后在一个Socket上进行循环,用来等待和处理ActivityManagerService服务向它发送创建新应用程序进程的请求,直至系统退出为止。
- 对于Android application进程,JVM是以android.os.Process类的静态成员函数main为入口点执行,然后在一消息队列上进行循环,用来等待和处理主线程的消息,直到应用程序退出为止。
Android JVM注册JNI 的过程:
- 根据libraryname找到对应的.so文件及路径;
- 调用native的loadlibray将so加载,并调用.so中的JNI_OnLoad();
- 将JavaVM中的native method与JNI表格中的对应method绑定: 将找到的native method地址填到JavaVM的nativefunc域;(这块涉及bridge type,后续再来梳理)。
Android JVM进程和线程的创建过程:
JVM的进程也是Linux的进程,只不过多了一个VM实例;JVM的线程是Linux的进程(线程,因为Linux没有thread)。创建过程分为四类:
JVM进程:通过android.os.Process的静态方法start()来创建;
JVM线程:通过java.lang.Thread的方法start()来创建;
只能执行native代码的线程:通过native的thread 的run()来创建;
能同时执行native 代码和Java代码的线程:通过native的thread 的run()来创建;
JVM进程的创建
- 调用Zygote.forkAndSpecialize来访问native层;
- 通过系统调用fork来创建一个新的进程,然后为新的进程设置uid、gid、gids、debugFlags和rlimits,从而将限制了新创建的进程的权限。
- 对JVM做初始化;
JVM线程的创建
- 调用VMThread的start来访问native层;
- 申请stacksize, 调用pthread_attr_init()来初始化thread attributes, 调用pthread_create()来生成thread, 新创建的线程会将状态设置为THREAD_STARTING。创建线程时候会将入口函数设置为interpThreadStart();
- interpThreadStart()做的事情有:
3.1 调用函数prepareThread来初始化新创建的JVM线程。
3.2 将新创建的JVM线程设置为THREAD_STARTING,以便其父线程,也就是创建它的线程可以继续往前执行。
3.3 通过一个while循环来等待父线程通知自己继续往前执行,也就是等待父线程将自己的状态设置为THREAD_VMWAIT。
3.4 为新创建的JVM线程创建一个JNI环境。
3.5 将新创建的JVM线程的状态设置为THREAD_RUNNING,表示它正式进入运行状态。
3.6 如果有调试器连接到当前JVM来了,这时候就要通知调试器新创建了一个线程。
3.7 设置新创建的JVM线程的优先级,这个优先级值保存在用来描述新创建的JVM线程的一个Java层Thread对象的成员变量priority中。
3.8 找到Java层的java.lang.Thread类的成员函数run,并且JVM执行,这个java.lang.Thread类的成员函数run即为JVM线程的Java代码入口点函数。
3.9 run执行完之后,新创建的JVM线程就完成自己的使命了,这时候就可以执行清理工作。
只能执行native代码线程的创建
我们一般都是使用Thread子类来创建Native线程的,这时候通过重写父类Thread的成员函数readyToRun和threadLoop,就可以使得新创建的Native线程主动地执行自定义的任务。
在Android系统中,Native线程和Dalvik虚拟机线程一样,都是通过pthread库提供的函数pthread_create来创建的,其中,参数entryFunction指向的就是新创建的Native线程的入口点。参数entryFunction指向的是Thread类的静态成员函数_threadLoop。
Thread类的静态成员函数_threadLoop的主体是一个while循环,这个while循环不断地调用Thread对象self的成员函数threadLoop,来作为前面所创建的Native线程的执行主体,直到满足以下三个条件之一:
1. Thread对象self的成员函数threadLoop的返回值等于false。
2. Thread对象self的成员函数threadLoop在执行的过程中,调用了另外一个成员函数requestExit请求前面所创建的Native线程退出,这时候Thread对象self的成员变量mExitPending的值就会等于true。
3. Thread对象self被销毁了,即强指针strong释放了对Thread对象self的强引用之后,弱指针weak不能成功地提升成强指针。
能同时执行native 代码和Java代码线程的创建
- 通过pthread库提供的函数pthread_create来创建一个线程,并且将AndroidRuntime类的静态成员函数javaThreadShell作为该新创建的线程的入口点函数。
- AndroidRuntime类的静态成员函数javaThreadShell主要是执行以三个操作:
2.1. 调用函数javaAttachThread来将当前线程附加到在当前进程中运行的JVM中去,使得当前线程不仅能够执行 C/C++代码,还可以执行Java代码。
2.2. 调用函数指针start所指向的函数来作为当前线程的执行主体。也就是Thread类的静态成员函数_threadLoop。因此,当前新创建的线程是以Thread类的静态成员函数_threadLoop作为执行主体的。
2.3. 从Thread类的静态成员函数_threadLoop返回来之后,当前线程就准备要退出了。在退出之前,需要调用函数javaDetachThread来将当前线程从当前进程中运行的JVM中移除。
更多相关文章
- C语言函数的递归(上)
- Android(安卓)下载一个文件以及打开这个apk文件
- android开发之onCreate( )方法详解
- Android浏览器开发 WebView setBlockNetworkImage来龙去脉
- android中自定义view构造函数ContentItemView(Context context,
- android 自定义属性
- Android(安卓)NDK纯C++开发(2)
- android 快速创建一个新的线程
- Android如何让Handler中内部run停止(runnable/thread)