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?

  1. Boot-loader loads kernel and start init process;
  2. Init start Zygote process;
  3. Zygote initializes a Dalvik/ART VM which preloads and pre-initializes core libraries classes;
  4. Zygote keeps in an idle state by system and waits for socket requests;
  5. 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中的启动过程

  1. 创建一个JavaJVM实例;
  2. 加载Java核心类及其JNI方法;
  3. 为主线程设置一个JNI 环境 JNIEnv;
  4. 注册Android核心类的JNI方法;

Android JVM的运行过程

  1. 对于Zygote进程,JVM以com.android.internal.os.ZygoteInit类的静态成员函数main为入口点执行,然后在一个Socket上进行循环,用来等待和处理ActivityManagerService服务向它发送创建新应用程序进程的请求,直至系统退出为止。
  2. 对于Android application进程,JVM是以android.os.Process类的静态成员函数main为入口点执行,然后在一消息队列上进行循环,用来等待和处理主线程的消息,直到应用程序退出为止。

Android JVM注册JNI 的过程:

  1. 根据libraryname找到对应的.so文件及路径;
  2. 调用native的loadlibray将so加载,并调用.so中的JNI_OnLoad();
  3. 将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进程的创建

  1. 调用Zygote.forkAndSpecialize来访问native层;
  2. 通过系统调用fork来创建一个新的进程,然后为新的进程设置uid、gid、gids、debugFlags和rlimits,从而将限制了新创建的进程的权限。
  3. 对JVM做初始化;

JVM线程的创建

  1. 调用VMThread的start来访问native层;
  2. 申请stacksize, 调用pthread_attr_init()来初始化thread attributes, 调用pthread_create()来生成thread, 新创建的线程会将状态设置为THREAD_STARTING。创建线程时候会将入口函数设置为interpThreadStart();
  3. 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代码线程的创建

  1. 通过pthread库提供的函数pthread_create来创建一个线程,并且将AndroidRuntime类的静态成员函数javaThreadShell作为该新创建的线程的入口点函数。
  2. AndroidRuntime类的静态成员函数javaThreadShell主要是执行以三个操作:
    2.1. 调用函数javaAttachThread来将当前线程附加到在当前进程中运行的JVM中去,使得当前线程不仅能够执行 C/C++代码,还可以执行Java代码。
    2.2. 调用函数指针start所指向的函数来作为当前线程的执行主体。也就是Thread类的静态成员函数_threadLoop。因此,当前新创建的线程是以Thread类的静态成员函数_threadLoop作为执行主体的。
    2.3. 从Thread类的静态成员函数_threadLoop返回来之后,当前线程就准备要退出了。在退出之前,需要调用函数javaDetachThread来将当前线程从当前进程中运行的JVM中移除。

更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)下载一个文件以及打开这个apk文件
  3. android开发之onCreate( )方法详解
  4. Android浏览器开发 WebView setBlockNetworkImage来龙去脉
  5. android中自定义view构造函数ContentItemView(Context context,
  6. android 自定义属性
  7. Android(安卓)NDK纯C++开发(2)
  8. android 快速创建一个新的线程
  9. Android如何让Handler中内部run停止(runnable/thread)

随机推荐

  1. Android(安卓)Service总结02 service介绍
  2. android merge和include简单使用
  3. 安卓,rebuild apk错误解决
  4. Android(安卓)的提权 (root) 原理是什么?
  5. xml中设置圆角矩形框
  6. Android中Activity启动模式launchermode
  7. 三个博客和一张关系图,掌握Android(安卓)W
  8. Android(安卓)studio 挂断电话 使用ITele
  9. 查看Android内存的8中方法
  10. Android(安卓)配置Material Design库及使