### **概述**
Android應用程序是運行在Dalvik虛擬機里面的,并且每一個應用程序對應有一個單獨的Dalvik虛擬機實例。Android應用程序中的Dalvik虛擬機實例實際上是從Zygote進程的地址空間拷貝而來的,這樣就可以加快Android應用程序的啟動速度。Dalvik虛擬機與Java虛擬機共享有差不多的特性,例如,它們都是解釋執行,并且支持即時編譯(JIT)、垃圾收集(GC)、Java本地方法調用(JNI)和Java遠程調試協議(JDWP)等,差別在于兩者執行的指令集是不一樣的,并且前者的指令集是基本寄存器的,而后者的指令集是基于堆棧的。
本系列文章主要將Dalvik虛擬機的內存管理、垃圾收集、即時編譯、Java本地調用、進程和線程管理等。理解Dalvik虛擬機的上述實現細節,有助于在運行時修改程序的行為,例如,攔截Java函數的調用。
* Dalvik虛擬機概述
* Dalvik虛擬機的啟動過程
* Dalvik虛擬機的運行過程
* JNI函數的注冊過程
* Dalvik虛擬機進程
* Dalvik虛擬機線程
#### **Dalvik虛擬機概述**
Dalvik虛擬機由Dan Bornstein開發,名字來源于他的祖先曾經居住過的位于冰島的同名小漁村
Dalvik虛擬機起源于Apache Harmony項目,后者是由Apache軟件基金會主導的,目標是實現一個獨立的、兼容JDK 5的虛擬機,并根據Apache License v2發布
**Dalvik虛擬機與Java虛擬機的區別**

* 基于堆棧的Java指令(1個字節)和基于寄存器的Dalvik指令(2、4或者6個字節)各有優劣
* 一般而言,執行同樣的功能, Java虛擬機需要更多的指令(主要是load和store指令),而Dalvik虛擬機需要更多的指令空間
* 需要更多指令意味著要多占用CPU時間,而需要更多指令空間意味著指令緩沖(i-cache)更易失效
* Dalvik虛擬機使用dex(Dalvik Executable)格式的類文件,而Java虛擬機使用class格式的類文件
* 一個dex文件可以包含若干個類,而一個class文件只包括一個類
* 由于一個dex文件可以包含若干個類,因此它可以將各個類中重復的字符串只保存一次,從而節省了空間,適合在內存有限的移動設備使用
* 一般來說,包含有相同類的未壓縮dex文件稍小于一個已經壓縮的jar文件
**Dex文件的生成**

**Dex文件的優化**
* 將invoke-virtual指令中的method index轉換為vtable index – 加快虛函數調用速度
* 將get/put指令中的field index轉換為byte offset – 加快實例成員變量訪問速度
* 將boolean/byte/char/short變種的get/put指令統一轉換為32位的get/put指令 – 減小VM解釋器的大小,從而更有效地利用CPU的i-cache
* 將高頻調用的函數,例如String.length,轉換為inline函數 – 消除函數調用開銷
* 移除空函數,例如Object.`<init>` -- 消除空函數調用
* 將可以預先計算的數據進行預處理,例如預先生成VM根據class name查詢class的hash table – 節省Dex文件加載時間以及內存占用空間
* 將invoke-virtual指令中的method index轉換為vtable index
~~~
invoke-virtual {v1, v2}, method@BBBB
→
invoke-virtual-quick {v1,v2},vtable #0xhh
~~~
* 將get/put指令中的field index轉換為byte offset
~~~
iget-object v0, v2, field@BBBB
→
iget-object-quick v0,v2,[obj+0x100]
~~~
備注:
http://www.haogongju.net/art/1171347
http://mylifewithandroid.blogspot.com/2009/05/about-quick-method-invocation.html
http://source.android.com/devices/tech/dalvik/dex-format.html
**Dex文件的優化時機**
* VM在運行時即時優化,例如使用DexClassLoader動態加載dex文件時。這時候需要指定一個當前進程有寫權限的用來保存odex的目錄。
* APP安裝時由具有root權限的installd優化。這時候優化產生的odex文件保存在特權目錄/data/dalvik-cache中。
* 編譯時優化。這時候編譯出來的jar/apk里面的classes.dex被提取并且優化為classes.odex保存在原jar/apk所在目錄,打包在system image中。
**內存管理**
* Java Object Heap
大小受限,16M/24M/32M/48M/…
* Bitmap Memory(External Memroy):
大小計入Java Object Heap
* Native Heap
大小不受限
**Java Object Heap**
* 用來分配Java對象。Dalvik虛擬機在啟動的時候,可以通過-Xms和-Xmx選項來指定Java Object Heap的最小值和最大值。
* Java Object Heap的最小和最大默認值為2M和16M。但是廠商會根據手機的配置情況進行調整,例如,G1、Droid、Nexus One和Xoom的Java Object Heap的最大值分別為16M、24M、32M 和48M。
* 通過ActivityManager.getMemoryClass可以獲得Dalvik虛擬機的Java Object Heap的最大值。
**Bitmap Memory**
* 用來處理圖像。在HoneyComb之前,Bitmap Memory是在Native Heap中分配的,但是這部分內存同樣計入Java Object Heap中。這就是為什么我們在調用BitmapFactory相關的接口來處理大圖像時,會拋出一個OutOfMemoryError異常的原因:**java.lang.OutOfMemoryError:?bitmap?size?exceeds?VM?budget**
* ?在HoneyComb以及更高的版本中,Bitmap Memory就直接是在Java Object Heap中分配了,這樣就可以直接接受GC的管理。
**Native Heap**
* 在Native Code中使用malloc等分配出來的內存,這部分內存不受Java Object Heap的大小限制。
* 注意,不要因為Native Heap可以自由使用就濫用,因為濫用Native Heap會導致系統可用內存急劇減少,從而引發系統采取激進的措施來Kill掉某些進程,用來補充可用內存,這樣會影響系統體驗。
**垃圾收集(GC)**
* Step 1: Mark,使用RootSet標記對象引用
* Step 2: Sweep,回收沒有被引用的對象
**GingerBread之前**
* Stop-the-word,也就是垃圾收集線程在執行的時候,其它的線程都停止
* ?Full heap collection,也就是一次收集完全部的垃圾
* 一次垃圾收集造成的程序中止時間通常都大于100ms
**GingerBread之后**
* Cocurrent,也就是大多數情況下,垃圾收集線程與其它線程是并發執行的
* ?Partial collection,也就是一次可能只收集一部分垃圾
* 一次垃圾收集造成的程序中止時間通常都小于5ms
* Dalvik虛擬機執行完成一次垃圾收集之后,我們通常可以看到類似以下的日志輸出:
~~~
D/dalvikvm(9050):?GC_CONCURRENT?freed?2049K,?65%?free?3571K/9991K,?external?4703K/5261K,?paused?2ms+2ms??
~~~
* GC_CONCURRENT表示并行GC,2049K表示總共回收的內存,3571K/9991K表示Java Object Heap統計,即在9991K的Java Object Heap中,有3571K是正在使用的,4703K/5261K表示External Memory統計,即在5261K的External Memory中,有4703K是正在使用的,2ms+2ms表示垃圾收集造成的程序中止時間
**即時編譯(JIT)**
* 從2.2開始支持JIT,并且是可選的,編譯時通過WITH_JIT宏進行控制
* 基于執行路徑(Executing Path)對熱門的代碼片斷進行優化(Trace JIT),傳統的Java虛擬機以Method為單位進行優化(Method JIT)
* 可以利用運行時信息進行激進優化,獲得比靜態編譯語言更高的性能,如Lazy Unlocking機制,可以參考《Oracle JRockit: The Definitive Guide》一書
* 實現原理:http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html
* 支持JDWP(Java Debug Wire Protocol)協議
* 每一個Dalvik虛擬機進程都都提供有一個端口來供調試器連接
* DDMS提供有一個轉發端口8870,通過它可以同時調試多個Dalvik虛擬機進程

#### **Dalvik虛擬機的啟動過程**
Dalvik虛擬機由Zygote進程啟動,然后再復制到System Server進程和應用程序進程

* startVM的過程中會創建一個JavaVMExt,并且該JavaVMExt關聯有一個JNIInvokeInterface,Native Code通過它來訪問Dalvik虛擬機

* startVM的過程中還會為當前線程關聯有一個JNIEnvExt,并且該JNIEnvExt 關聯有一個JNINativeInterface,Native Code通過它來調用Java函數或者訪問Java對象

**Dalvik虛擬機在Zygote進程啟動的過程中,還會進一步預加載Java和Android核心類庫以及系統資源**

**Dalvik虛擬機從Zygote進程復制到System Server進程之后,它們就通過COW(Copy On Write)機制共享同一個Dalvik虛擬機實例以及預加載類庫和資源**

**Dalvik虛擬機從Zygote進程復制到應用程序進程之后,它們同樣會通過COW(Copy On Write)機制共享同一個Dalvik虛擬機實例以及預加載類庫和資源**

#### **Dalvik虛擬機的運行過程**
* Dalvik虛擬機在Zygote進程中啟動之后,就會以ZygoteInit.main為入口點開始運行
* Dalvik虛擬機從Zygote進程復制到System Server進程之后,就會以SystemServer.main為入口點開始運行
* Dalvik虛擬機Zygote進程復制到應用程序進程之后,就會以ActivityThread.main為入口點開始運行
* 上述入口點都是通過調用JNINativeInterface接口的成員函數CallStaticVoidMethod來進入的
J
**JNINativeInterface->CallStaticVoidMethod對應的實現為CallStaticVoidMethodV**

**CallStaticVoidMethodV調用dvmCallMethodV**

**在Dalvik虛擬機中,無論是Java函數,還是Native函數,都是通過Method結構體來描述的**

> **備注**:
> struct Method定義在文件dalvik/vm/oo/Object.h中
**在Dalivk虛擬機中,通過dvmIsNativeMethod判斷一個函數是Java函數還是Native函數**

> **備注**:
> dvmIsNativeMethod定義在文件dalvik/vm/oo/Object.h中
**Native函數直接由CPU執行,Java函數由Dalvik虛擬機解釋執行,即通過dvmInterpret函數執行**

**Dalvik虛擬機標準解釋器:dvmInterpretStd**

**Invoke-direct指令由函數invokeDirect執行**

**函數invokeDirect調用?invokeMethod執行**

#### **JNI函數的注冊過程**
**JNI函數注冊示例 -- ClassWithJni**

**JNI函數注冊示例 -- shy_luo_jni_ClassWithJni_nanosleep**

**System.loadLibrary**

**Runtime.loadLibrary**

**Runtime.nativeLoad**

**dvmLoadNativeCode**

**JNI_OnLoad**

**jniRegisterNativeMethods**

**RegisterNatives**

**dvmRegisterJNIMethod**

**dvmUseJNIBridge**

**dvmSetNativeFunc**

#### **Dalvik虛擬機進程**
* Dalvik虛擬機進程與下層的Linux進程是一一對應的
* 當ActivityManagerService啟動一個組件的時候,發現用來運行該組件的應用程序進程不存在,就會請求Zygote進程創建
* Zygote進程通過調用Zygote類的成員函數forkAndSpecialize來創建
**Zygote.forkAndSpecialize**


**forkAndSpecializeCommon**

* Dalvik虛擬機線程與下層的Linux線程是一一對應的
* 在Java層中,可以創建一個Thread對象,并且調用該Thread對象的成員函數start來啟動一個Dalvik虛擬機線程
* 在Native層中,也可以通過創建一個Thread對象,并且調用該Thread對象的成員函數run來啟動一個Dalvik虛擬機線程
**在Java層創建Dalvik虛擬機線程--Thread.start**

**VMThread.create**

**dvmCreateInterpThread**

**線程啟動函數:interpThreadStart**

**dvmCreateJNIEnv**

**在Native層創建Dalvik虛擬機線程--Thread::run**

**createThreadEtc**

**androidCreateThreadEtc**

> **注意**,函數指針gCreateThreadFn所指向的函數在Dalvik虛擬機啟動時已經被修改為javaCreateThreadEtc
**javaCreateThreadEtc**

**androidCreateRawThreadEtc**

**AndroidRuntime::javaThreadShell**

**javaAttachThread**

**AttachCurrentThread**

**attachThread**

**dvmAttachCurrentThread**

- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析