一、Java虛擬機器(JVM)
1、JVM整體結構
- 使用javac將java檔案編譯成class檔案。
- 類載入器(ClassLoader)將class位元組碼載入進JVM對應的記憶體中。
- JVM將記憶體分配給方法區、堆區、棧區、本地方式棧4個部分,這4個部分分別儲存位元組碼不同的部分。
- 垃圾回收器(gc)會管理整個記憶體空間中的垃圾。
2、Java程式碼的編譯和執行過程
下圖是Java程式碼編譯的詳細流程(即,javac的執行過程),瞭解即可,一般只要知道java檔案是通過javac命令編譯成class檔案,再通過java命令執行的就可以了,如:
javac Hello.java
java Hello
複製程式碼
3、類載入器
1)Java中的類載入器
2)載入流程
- Loading:類的資訊從檔案中獲取並載入到JVM的記憶體中。
- Verifying:檢查讀入的結構是否符合JVM規範的描述。
- Preparing:分配一個結構用來儲存類資訊。
- Resolving:把類的常量池中的所有符號引用變成直接引用。
- Initializing:執行靜態初始化程式,把靜態變數初始化成指定的值。
4、記憶體管理
java中的記憶體管理指的是下圖中“記憶體空間”部分的記憶體操作。
1)Java棧區:
作用:存放java方法執行時所有的資料。 組成:由棧幀組成,一個棧幀代表一個方法的執行。
Java棧幀:每個方法從呼叫到執行完成就對應一個棧幀在虛擬機器棧中入棧到出棧。它描述了一個方法的區域性變數表、棧運算元、動態連結、方法出口。
2)本地方法棧
與Java棧區基於一致。
作用:本地方法棧是專門為native方法服務的。
3)方法區
儲存被虛擬機器載入的類資訊、常量、靜態常量、即時編譯器編譯後等資料(這些資料在程式啟動後會永遠佔據記憶體)。
4)堆區
作用:所有通過new建立的物件的記憶體都在堆中分配。 特點:是虛擬機器中最大的一塊記憶體,是GC要回收的部分。
對於堆區,其記憶體結構還有些不一樣的地方,先看下圖:
簡單來說,堆區分為新生代(Young Generation)與老年代(Old Generation),程式在創始物件時,物件會先被分配到新生代中,當新生代區記憶體不足時,JVM會通過一定的演算法規則將新生代中的物件轉移至老年代中,當新生代與老年代都沒有足夠的記憶體空間時,JVM就會丟擲OOM異常。
5、垃圾回收
1、垃圾收集演算法
1)引用記數演算法(jdk1.2之前)
在記憶體建立物件的同時,會為它建立一個引用記數器,並將引用記數器加1,每次有引用引用到此物件時,記數器就會累計加1,而當其中一個引用銷燬時,記數器就會減1,當引用記數器為0時,說明該物件已經是垃圾物件,下次gc時,物件就會被回收了。
弊端: 物件A與物件B互相引用時,這2個物件的引用記數器永遠是正數,當這2個物件都沒有被其他物件所引用時(物件不可達),會因為它們的引用記數器不為0導致它們不會被gc回收。
2)可達性演算法(jdk1.2 +)
也稱為根搜尋演算法。把程式所有的引用關係看做是一張圖(有向圖),從GC Root節點開始尋找所有的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點被認為是沒有引用的節點,即不可達的節點,就是垃圾物件。
上圖中ObjD、E、F因為沒有路徑可達,所以是垃圾物件。
2、引用的型別
java中的引用型別有4種:強引用、軟引用、弱引用、虛引用。其中,強引用和弱引用在開發中最常用。
弱引用的建立
// 強引用
Object obj = new Object();
// 弱引用,此時obj與wf都引用了Object物件
WeakReference<Object> wf = new WeakReference<Object>(obj);
// 斷開強引用,此時只有wf引用這個Object物件
obj = null;
// 通用弱引用獲取Object物件(可能為null)
wf.get();
複製程式碼
在使用wf.get()時,要判斷獲取到的物件是否為null,因為弱引用不會阻止物件的回收。
3、垃圾回收演算法
1)標記-清除演算法
從根集合遍歷所有的引用,上圖中,根集合引用了A,A引用了C,B是不可達的物件引用,在掃描階段中,B會被標記為垃圾物件,當垃圾回收機制執行時,會直接將B物件置為空,此時記憶體塊中就只剩下A、C物件引用,B就被垃圾回收給回收掉了。 優點:不需要進行物件的移動,僅對不存活的物件進行處理,在存活物件比較多的情況下極為高效。 缺點:由於標記-清除演算法會直接回收掉不存活的物件,會造成內在碎片,不利於後續物件的分配
2)複製演算法
從根集合開始遍歷,上圖中,遍歷到A時是可達的,就把A複製到另一塊空閒的記憶體中,繼續遍歷,發現B不可達,直接跳過,往後,發現C可達,就把C同樣地複製到這塊空閒記憶體中,等所有複製都處理完時,把原來的記憶體空間清空,只保留複製後的這塊記憶體空間。 優點:當存活物件比較少時,極為高效,且不會有產生記憶體碎片。 缺點:需要一塊記憶體作為交換空間來進行物件的移動。
3)標記-整理演算法
從根集合開始遍歷,通過對整個記憶體區的掃描,將可回收物件掃描出來,上圖中,到了第二階段,就將B標記為可回收物件,到了第三個階段,直接掃描並消除記憶體中被標記的物件,同時,在回收不存活物件佔用的空間時,會將記憶體中所有存活物件往左端空閒處移動,並更新對應的指標。
這三種演算法各有優劣,JVM在處理垃圾時會整合去使用,並不是只使用其中某個演算法。當記憶體中存活的物件比較少時,採用“複製演算法”去處理垃圾物件;當記憶體中存活的物件比較多時,會採用“標記-整理演算法”或“標記-清除演算法”去處理垃圾物件。
4、觸發回收
- Java虛擬機器無法再為新物件分配記憶體空間了
- 手動呼叫System.gc()方法(強烈不推薦,不會馬上執行,卻會加大虛擬機器壓力)
- 低優先順序的GC執行緒,被啟動了。
二、Android 虛擬機器
1、Dalvik VM 與 JVM 的不同
- 執行的檔案不同,一個是class,一個是dex。
- 類載入的系統與JVM區別較大。
- 可以同時存在多個DVM,但JVM只能存在一個。
- Dalvik是基於暫存器的,而JVM是基於棧的。
2、Dalvik VM 與 ART 的不同
- DVM使用JIT來將位元組碼轉換成機器碼,效率低。
- ART採用了AOT預編譯技術,執行速度更快。
- ART會佔用更多的應用安裝時間和儲存空間。