一、ART 和 Dalvik
JVM與Dalvik
Android應用程式運行在Dalvik/ART虛擬機,并且每一個應用程式對應有一個單獨的Dalvik虛擬機實體,Dalvik虛擬機實則也算是一個Java虛擬機,只不過它執行的不是class檔案,而是dex檔案,
Dalvik虛擬機與Java虛擬機共享有差不多的特性,差別在于兩者執行的指令集是不一樣的,前者的指令集是基本暫存器的,而后者的指令集是基于堆疊的,

基于堆疊的虛擬機
對于基于堆疊的虛擬機來說,每一個運行時的執行緒,都有一個獨立的堆疊,堆疊中記錄了方法呼叫的歷史,每有一次方法呼叫,堆疊中便會多一個堆疊楨,最頂部的堆疊楨稱作當前堆疊楨,其代表著當前執行的方法,基于堆疊的虛擬機通過運算元堆疊進行所有操作,

位元組碼指令

ICONST_1 : 將int型別常量1壓入運算元堆疊;
ISTORE 0 : 將堆疊頂int型別值存入區域變數0;
IADD : 執行int型別的加法 ;
暫存器
暫存器是CPU的組成部分,暫存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、資料和位址,

基于暫存器的虛擬機
基于暫存器的虛擬機中沒有運算元堆疊,但是有很多虛擬暫存器,其實和運算元堆疊相同,這些暫存器也存放在運行時堆疊中,本質上就是一個陣列,與JVM相似,在Dalvik VM中每個執行緒都有自己的PC和呼叫堆疊,方法呼叫的活動記錄以幀為單位保存在呼叫堆疊上,

與JVM版相比,可以發現Dalvik版程式的指令數明顯減少了,資料移動次數也明顯減少了,
沒有了jvm虛擬機中的區域變數變,沒有了資料在區域變數表和運算元堆疊之間過來過去,做的事情少,移動次數少,相當于把區域變數表和運算元堆疊合并了,
ART與Dalvik
Dalvik虛擬機執行的是dex位元組碼,解釋執行,從Android 2.2版本開始,支持JIT即時編譯(Just In Time)
在程式運行的程序中進行選擇熱點代碼(經常執行的代碼)進行編譯或者優化,
Dalvik是解釋執行結合即時執行
運行時編譯,編譯成機器碼,位元組碼還是需要虛擬機的翻譯,翻譯成機器能跑的機器碼,多一個翻譯的程序,本地的機器碼,是可以直接在機器跑的,
而ART(Android Runtime) 是在 Android 4.4 中引入的一個開發者選項,也是 Android 5.0 及更高版本的默認 Android 運行時,ART虛擬機執行的是本地機器碼,Android的運行時從Dalvik虛擬機替換成ART虛擬機,并不要求開發者將自己的應用直接編譯成目標機器碼,APK仍然是一個包含dex位元組碼的檔案,
ART看作一個Dalvik升級版本的虛擬機
dex2oat
那么,ART虛擬機執行的本地機器碼是從哪里來?
在安裝的時候過來的
安裝的時候執行dexopt的操作把dex檔案從apk中提取出來變成Odex檔案
ART的步驟變了,安裝的時候有一個dex2oat的操作,把位元組碼編譯成當前跑的機器的對應的機器碼,所以5.0,6.0安裝的時候會變慢
aot和jit對應(前者是運行之中編譯,后者運行之前編譯)
Dalvik下應用在安裝的程序,會執行一次優化,將dex位元組碼進行優化生成odex檔案,而Art下將應用的dex位元組碼翻譯成本地機器碼的最恰當AOT時機也就發生在應用安裝的時候,ART 引入了預先編譯機制(Ahead Of Time),在安裝時,ART 使用設備自帶的 dex2oat 工具來編譯應用,dex中的位元組碼將被編譯成本地機器碼,

到了7.0,8.0,9.0之后又變了沒有那么慢了,變成了混編
Android N的運作方式
ART 使用預先 (AOT) 編譯,并且從 Android N混合使用AOT編譯,解釋和JIT, 混合了這三個
1、最初安裝應用時不進行任何 AOT 編譯(安裝又快了),運行程序中解釋執行,對經常執行的方法進行JIT,經過 JIT 編譯的方法將會記錄到Profile組態檔中,
2、當設備閑置和充電時,編譯守護行程會運行,根據Profile檔案對常用代碼進行 AOT 編譯,待下次運行時直接使用

.art機器碼檔案
先判斷類在不在art中,在的話就不去dex中找了直接執行,如果不在就去dex中找然后解釋執行,像是快取
JIT編譯的代碼只對這次運行有效
Dalvik的dex就是dex
ART的odex就變成了機器碼
二、ClassLoader


InMemoryDexClassLoader是andorid 8.0出現的
主要了解PathClassLoader和DexClassLoader
還有一個BootClassLoader ,用于加載Android Framework層class檔案
String.class.getClassLoader() 就能得知String這個類被BootClassLoader 加載
Activity也是BootClassLoader 加載
AppCompatActivity不是系統的類,手機里沒有這個類,是參考來的第三方的類,android官方開發的類,是PathClassLoader加載的
class BootClassLoader extends ClassLoader {}
PathClassLoader 是Android 應用程式類加載器,整個程式的類加載器,任意的背景關系都可以呼叫getClassLoader()方法,得到的是整個程式的ClassLoader就是PathClassLoader
系統幫我們做了加載類
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
//找快取
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
責任鏈設計模式
PathClassLoader -------》parent:BootClassLoader
不是父類BaseDexClassLoader,而是PathClassLoader 里面的parent成員是BootClassLoader
PathClassLoader 加載自己的類
BootClassLoader 加載系統的類
雙親委托機制
某個類加載器在加載類時,首先將加載任務委托給父類加載器,依次遞回,如果父類加載器可以完成類加載任務,就成功回傳;只有父類加載器無法完成此加載任務或者沒有父類加載器時,才自己去加載,
1、避免重復加載,當父加載器已經加載了該類的時候,就沒有必要子ClassLoader再加載一次,
2、安全性考慮,防止核心API庫被隨意篡改,
先用c = parent.loadClass(name, false); BootClassLoader去找,找到就直接去用了
findClass
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
Class c = pathList.findClass(name, suppressedExceptions);
pathList
private final DexPathList pathList;
構造方法實體化的
public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
// TODO We should support giving this a library search path maybe.
super(parent);
this.pathList = new DexPathList(this, dexFiles);
}
DexPathList類:
一個dex就是一個Element物件,可能多個dex,所以陣列
private Element[] dexElements;
pathList.findClass
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
流程:找自己的類通過PathClassLoader
但是PathClassLoader沒有重新findClass
他的父類重寫了

apk檔案里面的dex檔案

三、熱修復
有bug的Demo類 修復后放到前面,在前面找到后就不管后面的了
熱修復要保證類沒有被加載過沒有被使用過,再執行熱修復,插入補丁包,加載過就有快取了就不會從dex中找了,所以必須在用之前修復
每次啟動都要熱修復

怎么查到陣列中去,要用反射
熱修復流程:
1.獲取到當前應用的PathClassLoader
2.反射獲取到DexPathList屬性物件pathList
3.反射修改pathList的dexElements
a.把補丁包patch.dex轉化為Element[ ](patch)
b.獲取pathList的dexElements屬性(old)
c.patch+old合并,并反射賦值給pathList的dexElements

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/261408.html
標籤:其他
下一篇:AndroidX遷移和方法
