Dalvik虚拟机执行流程

trace使用

  1. 在代码中加入调试命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.example.dalviktestapp;

    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Debug;

    public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    test();
    }

    public static void test() {
    // 调用Android调试模式下的调试方法
    Debug.startMethodTracing("/sdcard/test");
    System.out.println("Hello Dalvik");
    Debug.stopMethodTracing();
    }
    }

  2. AndroidMenifest.xml文件中加入权限

    1
    2
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>

  3. 右键项目->Debug As->Android Application,会在sdcard目录下生成trace文件

    1
    2
    $ adb pull /sdcard/test.trace . // 拷贝到本地
    $ traceview test.trace // 用traceview查看

可以看到程序中每个线程调用方法的启动和停止时间,分析程序的每一步流程,定位存在问题的代码。

Dalvik虚拟机执行流程

Dalvik虚拟机各部分功能:
线程管理:进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,所有的Android应用的线程都对应一个Linux线程,进程管理依赖于Zygote机制。
类加载:解析Dex文件并加载Dalvik字节码。
解释器:根据自身的指令集Dalvik ByteCode解释字节码。
内存管理:分配系统启动初始化和应用程序运行时需要的内存资源。
即时编译(Just-In-Time, JIT):在解释时动态地编译程序,以缓解解释器的低效工作。
本地方法调用(Java Native Interface, JNI):一套编程框架标准接口,允许Java代码和本地代码互相调用。
反射机制实现模块:允许程序在运行时透过Reflection API取得任何一个已知名称的类的内部信息,包括其描述符、超类、实现的接口,也包括属性和方法等所有信息,并可于运行时改变属性内容或调用内部方法。
调试支撑模块:Dalvik VM支持许多常见开发环境下的代码级调试,任何允许JDWP(Java Debug Wire Protocol——Java 调试线协议)下远程调试的工具都可以使用,其支持的调试器包括jdb、Eclipse、IntelliJ和JSwat。

Dalvik虚拟机运行在ARM平台的入口点

Dalvik虚拟机运行在ARM平台时,是从初始化进程加载的服务Zygote开始的。Zygote进程通过调用startVm来创建虚拟机。在这个函数中主要通过调用JNI_CreateJavaVM()来创建虚拟机。
代码清单 framework/base/core/jni/AndroidRuntime.cpp: startVm()

1
2
3
4
5
6
7
8
9
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
...
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
LOGE("JNI_CreateJavaVM failed\n");
goto bail;
}
...
}

Dalvik虚拟机的入口点通过调用JNI_CreateJavaVM()函数来完成虚拟机的创建。Java_CreateJavaVM所完成的主要工作如下。
(1) 检查JNI版本是否正确;
(2) 解析命令行参数,初始化JNIEnvJavaVM全局变量;
(3) 初始化全局变量gDvm
(4) 调用dvmStartup初始化虚拟机的各个模块,包括初始化垃圾收集器(GC)类加载器字节码校验模块解释器等,完成各模块的初始化后创建一个线程;
(5) 装载Dalvik虚拟机运行时核心类库并校验字节码。
JNI_CreateJavaVM函数的实现在文件dalvik/vm/Jni.cpp里
代码清单 dalvik/vm/Jni.cpp: JNI_CreateJavaVM()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
...
/* 初始化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);
LOGW("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 successded");
return JNI_OK;
}

Zygote进程

Zygote进程是由init进程根据system/core/rootdir/init.rc文件中的配置项创建的,init进程是系统启动后运行在用户空间的首个进程。init进程启动完系统所需的各种Daemon线程后,启动Zygote进程。Zygote进程启动后,Android的应用程序都由Zygote进程启动运行。Zygote主要负责:
(1) 启动系统服务system_server进程(Android绝大多数系统服务的守护进程)。
(2) 创建子进程运行Android应用程序。
Zygote工作流程:
(1) 系统init进程创建Zygote进程,通过执行app_process程序,开启Zygote进程。
(2) app_process生成AppRuntime对象,分析其主函数传递过来的参数,传递给AppRuntime对象,调用对象的start方法,在start中完成了以下三件事。

  • 调用startVm注册虚拟机。在其中通过调用JNI_CreateJavaVM()创建虚拟机。
  • 调用startReg注册JNI函数。注册虚拟机要使用的JNI函数,这样运行在虚拟机中的Java类就可以调用本地函数了。
  • 调用ZygoteInit类的main函数,运行ZygoteInit类(位于framework/base/core/java/com/android/internal/os/ZygoteInit.java)。
  1. ZygoteInit是Zygote的main函数入口,是Zygote的核心类,完成了Zygote的职责,其执行流程如下。
  • 调用registerZygoteSocket函数创建了一个socket接口,绑定socket套接字,接收新的Android应用程序运行请求。
  • 调用preloadClassespreloadResource函数加载Android application framework使用的类和资源。
  • 调用startSystemServer函数来启动SystemServer组件。在startSystemServer中调用forkSystemServer()来创建出一个名为system_server的进程,这个进程调用com.android.server.SystemServer的main函数,启动各项系统服务,并最终将调用线程加入到Binder通信系统。system_server是系统Service所驻留的进程,该进程是framework的核心。
  • Zygote从startSystemServer函数返回后,调用runSelectLoopMode函数进入一个无限循环,监听socket接口等待新的应用程序请求。

Zygote可以创建三种进程:forkZygote子进程,forkSystemServersystem_server进程,forkAndSpecialize非Zygote子进程
三种创建进程的方法根据JNI机制均对应有native方法,位于dalvik/vm/native/dalvik_system_Zygote.cpp文件中:
(1) fork
代码清单 dalvik/vm/native/dalvik_system_Zygote.cpp: Dalvik_dalvik_system_Zygote_fork()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* native public static int fork(); */
static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
{

pid_t pid;
// 判断当前虚拟机是否支持Zygote
if (!gDvm.zygote) {
dvmThrowIllegalStateException(
"VM instance not started with -Xzygote");
RETURN_VOID();
}
// 判断堆创建是否成功,不成功则停止虚拟机
if (!dvmGcPreZygoteFork()) {
ALOGE("pre-fork heap failed");
dvmAbort();
}
// 设置信号机制
setSignalHandler();
// 记录日志信息
dvmDumpLoaderStats("zygote");
pid = fork();

#ifdef HAVE_ANDROID_OS
if (pid == 0) {
/* child process */
extern int gMallocLeakZygoteChild;
gMallocLeakZygoteChild = 1;
}
#endif

RETURN_INT(pid);
}

新进程复制父进程的资源,新的Zygote进程的虚拟机参数中支持Zygote标记参数gDvm.zygote也为true,因而也可以fork子进程。
(2) forkSystemServer()
代码清单 dalvik/vm/native/dalvik_system_Zygote.cpp: Dalvik_dalvik_system_Zygote_forkSystemServer()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
* native public static int nativeForkSystemServer(int uid, int gid,
* int[] gids, int debugFlags, int[][] rlimits,
* long permittedCapabilities, long effectiveCapabilities);
*/

static void Dalvik_dalvik_system_Zygote_forkSystemServer(
const u4* args, JValue* pResult)

{

pid_t pid;
// 根据参数,fork一个子进程
pid = forkAndSpecializeCommon(args, true);
/* The zygote process checks whether the child process has died or not. */
if (pid > 0) {
int status;
ALOGI("System server process %d has been created", pid);
// 保存system_server的进程id
gDvm.systemServerPid = pid;
/* There is a slight window that the system server process has crashed
* but it went unnoticed because we haven't published its pid yet. So
* we recheck here just to make sure that all is well.
*/

if (waitpid(pid, &status, WNOHANG) == pid) {
// 如果system_server退出了,Zygote直接kill自己
ALOGE("System server process %d has died. Restarting Zygote!", pid);
kill(getpid(), SIGKILL);
}
}
RETURN_INT(pid);
}

system_server子进程一直驻留在系统中,一旦此进程退出,父进程Zygote也会被终止。系统init进程通过重启Zygote进程,使得Zygote进程重新启动system_server进程。
(3) forkAndSpecialize()
代码清单 dalvik/vm/native/dalvik_system_Zygote.cpp: Dalvik_dalvik_system_Zygote_forkAndSpecialize()

1
2
3
4
5
6
7
8
9
10
11
/*
* native public static int nativeForkAndSpecialize(int uid, int gid,
* int[] gids, int debugFlags, int[][] rlimits, int mountExternal,
* String seInfo, String niceName);
*/

static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4 *args, JValue *pResult)
{

pid_t pid;
pid = forkAndSpecializeCommon(args, false);
RETURN_INT(pid);
}

forkAndSpecializeCommon()函数的第二个参数标识创建的子进程是否为system_server子进程。forkSystemServer()调用时第二个参数为true,而forkAndSpecialize()调用时为false。
代码清单 dalvik/vm/native/dalvik_system_Zygote.cpp: forkAndSpecializeCommon()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
* Utility routine to fork zygote and specialize the child process.
*/

static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{

pid_t pid;

uid_t uid = (uid_t) args[0];
gid_t gid = (gid_t) args[1];
ArrayObject* gids = (ArrayObject *)args[2];
u4 debugFlags = args[3];
ArrayObject *rlimits = (ArrayObject *)args[4];
u4 mountMode = MOUNT_EXTERNAL_NONE;
int64_t permittedCapabilities, effectiveCapabilities;
char *seInfo = NULL;
char *niceName = NULL;
// 判断是否为system_server进程
if (isSystemServer) {
permittedCapabilities = args[5] | (int64_t) args[6] << 32;
effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
} else {
mountMode = args[5];
permittedCapabilities = effectiveCapabilities = 0;
StringObject* seInfoObj = (StringObject*)args[6];
if (seInfoObj) {
seInfo = dvmCreateCstrFromString(seInfoObj);
if (!seInfo) {
ALOGE("seInfo dvmCreateCstrFromString failed");
dvmAbort();
}
}
StringObject* niceNameObj = (StringObject*)args[7];
if (niceNameObj) {
niceName = dvmCreateCstrFromString(niceNameObj);
if (!niceName) {
ALOGE("niceName dvmCreateCstrFromString failed");
dvmAbort();
}
}
}
// 判断当前虚拟机是否支持Zygote
if (!gDvm.zygote) {
dvmThrowIllegalStateException(
"VM instance not started with -Xzygote");
return -1;
}
// 判断堆创建是否成功,不成功则停止虚拟机
if (!dvmGcPreZygoteFork()) {
ALOGE("pre-fork heap failed");
dvmAbort();
}
// 设置信号处理
setSignalHandler();
dvmDumpLoaderStats("zygote");

pid = fork();
if (pid == 0) {
int err;
/* The child process */
#ifdef HAVE_ANDROID_OS
extern int gMallocLeakZygoteChild;
gMallocLeakZygoteChild = 1;

/* keep caps across UID change, unless we're staying root */
if (uid != 0) {
err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);

if (err < 0) {
ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
dvmAbort();
}
}

for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
if (err < 0) {
if (errno == EINVAL) {
ALOGW("PR_CAPBSET_DROP %d failed: %s. "
"Please make sure your kernel is compiled with "
"file capabilities support enabled.",
i, strerror(errno));
} else {
ALOGE("PR_CAPBSET_DROP %d failed: %s.", i, strerror(errno));
dvmAbort();
}
}
}

#endif /* HAVE_ANDROID_OS */

if (mountMode != MOUNT_EXTERNAL_NONE) {
err = mountEmulatedStorage(uid, mountMode);
if (err < 0) {
ALOGE("cannot mountExternalStorage(): %s", strerror(errno));

if (errno == ENOTCONN || errno == EROFS) {
// When device is actively encrypting, we get ENOTCONN here
// since FUSE was mounted before the framework restarted.
// When encrypted device is booting, we get EROFS since
// FUSE hasn't been created yet by init.
// In either case, continue without external storage.
} else {
dvmAbort();
}
}
}
// 将list数组中所标明的组加入到目前进程的组设置中
err = setgroupsIntarray(gids);
if (err < 0) {
ALOGE("cannot setgroups(): %s", strerror(errno));
dvmAbort();
}
// 设置资源限制
err = setrlimitsFromArray(rlimits);
if (err < 0) {
ALOGE("cannot setrlimit(): %s", strerror(errno));
dvmAbort();
}
// 设置指定进程组id
err = setresgid(gid, gid, gid);
if (err < 0) {
ALOGE("cannot setresgid(%d): %s", gid, strerror(errno));
dvmAbort();
}
// 设置用户id
err = setresuid(uid, uid, uid);
if (err < 0) {
ALOGE("cannot setresuid(%d): %s", uid, strerror(errno));
dvmAbort();
}

if (needsNoRandomizeWorkaround()) {
int current = personality(0xffffFFFF);
int success = personality((ADDR_NO_RANDOMIZE | current));
if (success == -1) {
ALOGW("Personality switch failed. current=%d error=%d\n", current, errno);
}
}
// 设置Linux功能标识
err = setCapabilities(permittedCapabilities, effectiveCapabilities);
if (err != 0) {
ALOGE("cannot set capabilities (%llx,%llx): %s",
permittedCapabilities, effectiveCapabilities, strerror(err));
dvmAbort();
}

err = set_sched_policy(0, SP_DEFAULT);
if (err < 0) {
ALOGE("cannot set_sched_policy(0, SP_DEFAULT): %s", strerror(-err));
dvmAbort();
}

err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
if (err < 0) {
ALOGE("cannot set SELinux context: %s\n", strerror(errno));
dvmAbort();
}
// These free(3) calls are safe because we know we're only ever forking
// a single-threaded process, so we know no other thread held the heap
// lock when we forked.
free(seInfo);
free(niceName);

/*
* Our system thread ID has changed. Get the new one.
*/

Thread* thread = dvmThreadSelf();
thread->systemTid = dvmGetSysThreadId();

/* configure additional debug options */
enableDebugFeatures(debugFlags);
// 将信号量机制设为默认
unsetSignalHandler();
// 子进程不支持Zygote
gDvm.zygote = false;
// 检查虚拟机初始化是否成功
if (!dvmInitAfterZygote()) {
ALOGE("error in post-zygote initialization");
dvmAbort();
}
} else if (pid > 0) {
/* the parent process */
free(seInfo);
free(niceName);
}

return pid;
}

forkAndSpcializeCommon()函数与Dalvik_dalvik_system_Zygote_fork()函数流程的不同之处:

  • 在forkAndSpecializeCommon()函数开始时,需要根据参数isSystemServer判断所需创建的子进程是否为system_server,并对其参数进行处理。
  • 由于调用forkAndSpecializeCommon()函数产生的子进程都不是Zygote进程,因此,要调用unsetSignalHandler()函数重置信号机制。同时,子进程不能创建新进程,故设置为不支持Zygote:gDvm.zygote = false
  • 子进程中还要检查虚拟机是否初始化成功,因为需要执行相应任务,所以必须保证虚拟机成功初始化。

Dalvik虚拟机运行应用程序

应用程序是以apk文件形式被虚拟机通过类加载模块引用加载并提取可执行代码的。apk文件解压后得到Dex文件,类加载模块对Dex文件进行解析,将所需的类的各个信息抓取出来,并将其封装到一个数据结构实例对象中,以供解释器直接引用这个结构体对象的相关的成员变量,实现程序的实际运行。
类加载模块的工作主要分为以下两个阶段:
(1) 取得Dex原始文件将其还原到内存中,并将Dex文件与一个DexFile结构体对象关联。
(2) 对Dex文件中的各个类依次进行加载生成类对象实例,并将对象指针交付给解释器引用执行。
在Dalvik上执行的程序由字节码组成,在字节码加载已经完毕后,Dalvik虚拟机解释器被调用开始取指解释执行。解释器有两种实现:C语言实现和汇编语言实现,分别称为Portable解释器Fast解释器
为了缓解解释器的低效,有两种解决方法:

  • 采用NDK,调用静态编译的方法提高效率。
  • 使用JIT,在运行时编译字节码并优化。 JIT更具一般性和可移植性。JIT混合了两种技术,解释器解释时编译部分程序。