Android ART模式簡介

新書上市《深入解析Android 5.0系統》

 以下內容節選自本書


Android4.4最大的變化就是引入ART模式來代替Dalvik虛擬機。ART是AndroidRuntime的縮寫,它提供了以AOT(Ahead-Of-Time)的方式運行Android應用程序的機制。所謂AOT是指在運行前就把中間代碼靜態編譯成本地代碼,這就節省了JIT運行時的轉換時間。因此,和採用JIT的Dalvik相比,ART模式在總體性能有了很大的提升,應用程序不但運行效率更高,耗電量更低,而且佔用的內存也更少
ART和dalvik相比,系統的性能得到了顯著提升,同時佔用的內存更少,因此能支持配置更低的設備。但是ART模式下編譯出來的文件會比以前增大10%-20%,系統需要更多的存儲空間,同時因爲在安裝時要執行編譯,應用的安裝時間也比以前更長了。


ART和dalvik相比,系統的性能得到了顯著提升,同時佔用的內存更少,因此能支持配置更低的設備。但是ART模式下編譯出來的文件會比以前增大10%-20%,系統需要更多的存儲空間,同時因爲在安裝時要執行編譯,應用的安裝時間也比以前更長了。

 

開啓ART模式


在Android中ART模式缺省是關閉的,如果我們希望啓動ART模式,需要在“設置”應用中手工打開它。進入“設置”後,選擇“開發者選項”,再點擊“選擇運行環境”,會彈出如選擇DALVIK或ART的對話框。選擇ART後,系統將會重新啓動,然後系統的運行環境就會從Dalvik切換到ART模式。
讓我們打開“設置”應用的代碼,看看這種切換到底做了些什麼。下面是DevelopmentSettings.java中的一段代碼:
   builder.setPositiveButton(android.R.string.ok, newOnClickListener() {
       @Override
       public void onClick(DialogInterface dialog, int which) {
            SystemProperties.set(SELECT_RUNTIME_PROPERTY,newRuntimeValue);
            pokeSystemProperties();
            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
            pm.reboot(null);
        }
    });
上面這段代碼只是設置重新設置了屬性SELECT_RUNTIME_PROPERTY的值,然後就重啓設備。這個屬性的定義如下:
String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib"
注意屬性是以persist開頭的,所以它的值在重新開機後也可以保存。這個屬性定義的是當前使用的運行環境庫,如果是dalvik,它的值爲libdvm.so,如果是art,它的值爲libart.so。通過這個屬性,系統就能判斷出需要運行的模式。
ART系統的源碼位於目錄art下,包含了以下目錄:
build目錄:存放了一些mk文件。
compiler目錄:動態庫libart-compiler.so的源碼目錄,它包含了一個ART的編譯器。
dalvikvm目錄:可執行文件dalvikvm的源碼目錄。
dex2oat目錄:可執行文件dex2oat的源碼目錄。用來將dex格式的文件編譯成可執行文件。dex2oat文件會鏈接libart-compiler動態庫。
jdwpspy目錄:可執行文件jdwpspy的源碼目錄。這是一個調試工具,通過socket接收VM中輸出的消息並打印輸出。
oatdump目錄:可執行文件oatdump的源碼目錄。這是一個工具,能dump出art文件的信息。
runtime目錄:動態庫libart.so的源碼目錄。
test目錄:包含了大量測試代碼的目錄。
tools目錄:包含了幾個腳本文件。

 

兩種模式的區別


從系統的實現角度看,ART和Dalvik的區別只表現在兩個地方。一處是在安裝應用執行優化時,Dalvik模式下執行的是dexopt程序,而ART模式下執行的是dex2oat程序。這個在“11.5.5節dexopt(優化)命令”中已經介紹過來。通過dex2oat程序編譯後得到的是elf格式的可執行文件,而不是以前的dalvik字節碼了。
另一處是在運行應用時,Dalvik模式下系統爲應用進程鏈接的是libdvm.so,而ART模式下鏈接的是libart.so。這兩個庫文件能夠相互替換,是因爲它們都實現了相同的接口。下面我們看看Android是如何來裝載不同運行庫的。
前面我們已經介紹過了,Zygote進程啓動時會調用AndroidRuntime的start()函數來啓動虛擬機,start()函數中有下面兩行代碼:
void AndroidRuntime::start(const char* className, const char*options)
{
    ......
   JniInvocation jni_invocation;
   jni_invocation.Init(NULL);
    ......
}
我們看看jni_invocation的Init()函數的代碼:
bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
  char default_library[PROPERTY_VALUE_MAX]; //從property中得到運行庫的名稱
  property_get(kLibrarySystemProperty,default_library, kLibraryFallback);
#else
  const char* default_library =kLibraryFallback;
#endif
  if (library == NULL) {
    library =default_library;
  }
  handle_ = dlopen(library,RTLD_NOW); // 裝載運行庫
  if (handle_ == NULL) {
    ...... //錯誤處理
  }
  // 裝載三個VM的入口函數。
  if(!FindSymbol(reinterpret_cast(&JNI_GetDefaultJavaVMInitArgs_),
                 "JNI_GetDefaultJavaVMInitArgs")) {
    returnfalse;
  }
  if(!FindSymbol(reinterpret_cast(&JNI_CreateJavaVM_),
                 "JNI_CreateJavaVM")) {
    returnfalse;
  }
  if(!FindSymbol(reinterpret_cast(&JNI_GetCreatedJavaVMs_),
                 "JNI_GetCreatedJavaVMs")) {
    returnfalse;
  }
  return true;
}
JniInvocation的Init()函數中通過屬性persist.sys.dalvik.vm.lib取得了運行庫的名稱,然後調用dlopen()來裝載運行庫,同時從動態庫中讀取了三個函數的地址,它們是JNI_GetDefaultJavaVMInitArgs(),JNI_CreateJavaVM()和JNI_GetCreatedJavaVMs()。這三個函數相當於是虛擬機的三個入口函數,通過調用它們,就能創建出虛擬機系統。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章