Android Dex vs ART 虚拟机运行效率提升

Android 4.4提供了一种与Dalvik截然不同的运行环境ART(Android runtime)支持,ART源于google收购的Flexycore的公司。ART模式与Dalvik模式最大的不同在于,启用ART模式后,系统在安装应用的时候会进行一次预编译,将字节码转换为机器语言存储在本地,这样在运行程序时就不会每次都进行一次编译了,执行效率也大大提升。

虚拟机切换设置

Settings> Developer Options> Select Runtime


JniInvocation提供统一了接口:
libnativehelper\JniInvocation.cpp
?
1
2
3
4
5
6
7
8
9
10
11
jint JniInvocation::JNI_GetDefaultJavaVMInitArgs( void * vmargs) {
   return JNI_GetDefaultJavaVMInitArgs_(vmargs);
}
 
jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void * vm_args) {
   return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
}
 
jint JniInvocation::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
   return JNI_GetCreatedJavaVMs_(vms, size, vm_count);
}
该函数间接调用以下函数来实现:
?
1
2
3
4
5
6
7
8
9
10
11
extern "C" jint JNI_GetDefaultJavaVMInitArgs( void * vm_args) {
   return JniInvocation::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args);
}
 
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void * vm_args) {
   return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
 
extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
   return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
}
libnativehelper\JniInvocation.h
?
1
2
3
4
5
static JniInvocation* jni_invocation_;
void * handle_; //保存so库的首地址
jint (*JNI_GetDefaultJavaVMInitArgs_)( void *);
jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void *);
jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*);
因此JniInvocation对外提供的接口函数是由JniInvocation中定义的函数指针来实现。JniInvocation中定义的函数指针是在哪里初始化的呢?我们知道 Android虚拟机是在Zygote进程中通过调用AndroidRuntime::start()函数启动的:frameworks\base\core\jni\AndroidRuntime.cpp
?
1
2
3
4
5
6
7
8
9
10
11
12
13
void AndroidRuntime::start( const char * className, const char * options)
{
    ...
     /* start the virtual machine */
     JniInvocation jni_invocation;
     jni_invocation.Init(NULL);
     JNIEnv* env;
     if (startVm(&mJavaVM, &env) != 0 ) {
         return ;
     }
     onVmCreated(env);
     ...
}
在启动虚拟机前通过调用JniInvocation::Init()函数来初始化JniInvocation中声明的函数指针
?
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
bool JniInvocation::Init( const char * library) {
#ifdef HAVE_ANDROID_OS
   char default_library[PROPERTY_VALUE_MAX];
   property_get( "persist.sys.dalvik.vm.lib" , default_library, "libdvm.so" );
# else
   const char * default_library = "libdvm.so" ;
#endif
   if (library == NULL) {
     library = default_library;
   }
 
   handle_ = dlopen(library, RTLD_NOW);
   if (handle_ == NULL) {
     ALOGE( "Failed to dlopen %s: %s" , library, dlerror());
     return false ;
   }
   //查找JNI_GetDefaultJavaVMInitArgs函数指针
   if (!FindSymbol(reinterpret_cast< void **>(&JNI_GetDefaultJavaVMInitArgs_),
                   "JNI_GetDefaultJavaVMInitArgs" )) {
     return false ;
   }
   //查找JNI_CreateJavaVM函数指针
   if (!FindSymbol(reinterpret_cast< void **>(&JNI_CreateJavaVM_),
                   "JNI_CreateJavaVM" )) {
     return false ;
   }
   //查找JNI_GetCreatedJavaVMs函数指针
   if (!FindSymbol(reinterpret_cast< void **>(&JNI_GetCreatedJavaVMs_),
                   "JNI_GetCreatedJavaVMs" )) {
     return false ;
   }
   return true ;
}</ void **></ void **></ void **>
该函数就是根据persist.sys.dalvik.vm.lib属性值来加载不同的虚拟机动态库,并从动态库中查找JNI_GetDefaultJavaVMInitArgs,JNI_CreateJavaVM,JNI_GetCreatedJavaVMs函数首地址
?
1
2
3
4
5
6
7
8
9
10
bool JniInvocation::FindSymbol( void ** pointer, const char * symbol) {
   *pointer = dlsym(handle_, symbol);
   if (*pointer == NULL) {
     ALOGE( "Failed to find symbol %s: %s\n" , symbol, dlerror());
     dlclose(handle_);
     handle_ = NULL;
     return false ;
   }
   return true ;
}

Dalvik字节码生成过程

应用安装时采用的代码优化方式不同:
Dalvik : dex2opt
ART : dex2oat
\

ART本地码生成过程

Dalvik dex代码经过dex => optimized dex => JIT cache这个过程,内存中需要同时容纳odex和JIT cache两份代码;换成ART以后,就变成dex => oat,内存里只放oat就可以。


\

两个运行环境产生的优化代码路径及文件名都为:/data/dalvik-cache/app/data@app@{package name}.apk@classes.dex,但是ART环境产生的优化代码文件大小明显比Dalvik环境产生大。OAT文件其实就是基于ELF格式的一种私有文件格式。

dex2oat编译过程

开发者开发出的应用程序经过编译和打包之后,仍然是一个包含dex字节码的APK文件。既然应用程序包含的仍然是dex字节码,而ART虚拟机需要的是本地机器码,这就必然要有一个翻译的过程。Android系统在应用程序安装时,通过dex2oat工具将应用的dex字节码翻译成本地机器码。


\

ART相关源代码:


OAT文件加载流程

1、读取oatdata符号地址获取Oat数据 startAddress。

2、读取oatlastword符号地址获取OAT数据 endAddress。

3、通过startAddress和endAddress定位Oat数据。

4、解析Oat数据。构建方法定位所需数据结构。

然后就可以调用加载OAT文件的代码了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值