Dalvik名字來源于其作者祖先居住的小村莊,老外喜歡起這種名字,類似的還有ubuntu、Kali,雖說現在使用ART取代了Dalvik,但是感覺簡單學習一下還是有用的。
一、.與java虛拟機對比
Java虛拟機解析class檔案,Dalvik虛拟機解析dex(dalvik executable)檔案
android SDK 的dx工具可以将java位元組碼轉換為Dalvik位元組碼,對java類檔案進行了壓縮,去除了備援資訊,是以體積更小
架構不同,Java虛拟機基于棧結構,Dalvik基于寄存器架構,對于手機裝置來說後者更适用,并且速度更快
下面以Hello.java 為示例檔案,分析兩種位元組碼的差別
public class Hello {
public int foo(int a,int b){
return (a+b)*(a-b);
}
public static void main(String[] argc){
Hello hello=new Hello();
System.out.println(hello.foo(5,3));
}
}
1. javac Hello.java 将其編譯為Hello.class
2. ./dx --dex --output=Hello.dex Hello.class 将class檔案轉化為dex檔案
3. javap -c -classpath . Hello得到反編譯代碼,這是jvm指令集,這裡隻列出foo函數對應代碼。
java位元組碼一個指令為一個位元組,PC計數器以位元組為機關記錄指令偏移量,圖中PC對應的指令為iadd,前兩行指令取出了函數兩個參數并加載到求值棧
4.dexdump -d Hello.dex 得到的是dalvik指令集代碼,這裡隻列出foo函數對應代碼,左邊的上半部分和下半部分分别對應指令的十六進制和助記符格式。Dalvik維護一個pc計數器和一個調用棧,但是這個調用棧維護的是一份寄存器清單。
二、安卓系統如何啟動及dalvik虛拟機如何運作
1.系統架構以及運作流程
(1)Loader層:加載和運作引導程式
boot ROM:開機時,引導晶片從rom中預設的代碼開始執行,然後将引導程式加載到ram。
boot loader:運作引導程式,主要是檢查ram、初始化參數等。
(2)kernel層:Android核心層,在這裡開機剛剛完成進入系統
啟動swapper程序(pid=0的程序),這是系統初始化過程kernel建立的第一個程序,用于初始化程序管理、記憶體管理、加載驅動等工作
啟動kthreadd程序,這是linux系統的核心程序,會建立核心工作線程kworkder、軟中斷程序ksoftirqd和thermal等核心守護程序,kthreadd是所有核心程序的父程序。
(3)native層(C++ Framework層):包含C++庫、硬體抽象層(HAL)、Dalvik虛拟機或者安卓運作時(ART)
由init程序孵化出使用者空間的守護程序、開機動畫、hal層等。init是linux的守護程序,是所有使用者空間程序的父程序.
init程序孵化出MediaServer程序,負責啟動和管理C++Framework層,包含AudioFlinger,Camera Service等服務。
特别的,init程序會解析init.rc檔案,然後孵化出zygote程序,zygote程序是Android系統的第一個java程序(虛拟機程序),zygote是所有java程序的父程序
(4)java Framework層(application Framework層):這一層由java語言編寫
zygote程序負責加載ZygoteInit類、加載虛拟機、提前加載類preloadClasses、提前加載資源preloadResource
zygote程序孵化出System Server程序,它負責啟動和管理整個Java Framework,包含ActivityManager、PackageManager、WindowManager等服務
(5)App層:這一層與使用者直接互動,應用使用的是java語言開發
Zygote程序孵化出的第一個App程序是Luncher,就是桌面App,然後孵化出browser、phone、Email等基礎的App程序,一個App至少運作在一個程序上。
所有的App程序都是由Zygote程序fork而來
(6)Syscall和JNI
Native和kernel之間是系統調用(Syscall)層
Java層與Native層之間的紐帶是JNI(Java Native Interface)
2.dalvik虛拟機如何運作
上文已經介紹了Zygote程序,Zygote fork出其他程序之後,執行的工作就有Dalvik來進行。
它首先通過LoadClassFromDex()函數完成類的裝載工作,類被解析之後有一個ClassObject類型的資料結構儲存在運作時環境中,虛拟機使用gDvm.loadedClasses全局哈希表來儲存和查詢所有裝載的類;
位元組碼驗證器使用verifyCodeFlow()函數對裝入的代碼進行校驗,随後虛拟機調用FindClass()函數查找和裝載main方法類,随後調用dvmInterpret()函數初始化解釋器并執行位元組碼流
3.Dalvik虛拟機JIT(just in time 即時編譯)機制
JIT又稱為動态編譯,是一種在運作時将位元組碼翻譯為機器碼的技術,這使得程式執行速度更快
JIT有兩種代碼編譯方式:Method方式和trace方式。分别以函數為機關和以trace為機關進行編譯。簡單說一下trace方式,函數的代碼被分為許多條執行路徑,根據執行的頻繁與否被分為熱路徑和冷路徑。trace方式能夠快速的擷取熱路徑,以更短的時間和更少的記憶體來編譯代碼
三、Dalik指令
Dalvik指令語言其實就是smali語言,指令這部分有點多,回頭再研究
smali檔案執行個體
.class public LHelloWorld; #定義類名
.super Ljava/lang/Object; #定義父類
.method public static main([Ljava/lang/String;)V
.registers 4 #程式中使用4個寄存器,v0,v1,v2和一個參數寄存器
.parameter #一個參數,有n個參數則有n行
.prologue #代碼起始的指令
#空指令
nop
nop
nop
nop
#資料定義指令
const/16 v0, 0x8
const/4 v1, 0x5
const/4 v2, 0x3
#資料操作指令
move v1, v2
#數組操作指令
new-array v0, v0, [I
array-length v1, v0
#執行個體操作指令
new-instance v1, Ljava/lang/StringBuilder;
#方法調用指令
invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
#跳轉指令
if-nez v0, :cond_0
goto :goto_0
:cond_0
#資料轉換指令
int-to-float v2, v2
#資料運算指令
add-float v2, v2, v2
#比較指令
cmpl-float v0, v2, v2
#字段操作指令
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World" #構造字元串
#方法調用指令
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
#傳回指令
:goto_0
return-void
.end method
編譯smali檔案,生成dex檔案
java-jar smali.jar -o classes.dex Helloworld.smali
在真機或者虛拟機測試運作
先将classes.dex壓縮為Helloworld.zip
adb push Helloworld.zip /data/local/tmp
adb shell dalvikvm -cp /data/local/tmp/Helloworld.zip HelloWorld
正常運作會列印helloworld字元串