JVM虛擬機的整體結構
- 先看一張圖
- class文件生成模塊:jdk中的javac編譯命令
- 類加載器子系統:將class字節碼加載到內存空間中,核心就是ClassLoader,動態更新的核心。
- 內存空間包括:方法區()、Java堆、Java棧、本地方法棧。用來存儲class字節碼不同的部分。
- 垃圾收集器: (gc)
這四個模塊是我們程序員經常打交道的,理解以上四個模塊,就可以說對JVM有個深入的瞭解了。其他的模塊:
指令計數器,執行引擎,本地方法接口等,都是JVM比較底層的與CPU打交道的接口了,不需要我們過多的瞭解。
JVM爲我們提供的所有類加載器:
- BootStrapClassLoader:是用來加載JRE\lib\rt.jar或者-Xbootclasspath選項指定的jar包。
- ExtensionClassLoader:是用來加載JRE\lib\ext*.jar或-Djava.ext.dirs指定目錄下的jar包。 它和上面的BootStrapClassLoader是用來加載JDK中特定的jar包的。
- AppClassLoader:這個是我們應用程序的ClassLoader。
- CustomClassLoader:自定義的ClassLoader。
Java代碼的編譯和執行過程
- classloader加載流程:
- Loading:從類文件中獲取類的信息並且加載到JVM內存裏,真正的把class字節碼加載到內存中。
- Verifying:驗證,檢查讀入的字節碼結構是否符合JVM規範的描述。
- Preparing:分配一個數據結構來存儲類信息。
- Resolving:把類的常量池的所有符號引用改變成直接引用。
- Initializing:執行靜態初始化程序,把靜態變量初始化成指定的值。
內存管理和垃圾回收
- java棧區:java內存管理中最重要的模塊
- 作用:它存放的是java方法執時的所有數據。是描述java方法執行的完整的內存模型。
我們類中的方法,在執行的時候是被嵌套調用的,也就是方法a調用方法b,方法b在調用方法c,一直嵌套然後返回。那我們棧區是如何完整
的描述這個過程呢?這時候就引出了棧幀。把棧幀理解清楚了,就自然瞭解了java棧區。
- 組成:由棧幀組成,一個棧幀代表一個方法的執行。
- 棧幀:
- 作用:每個方法從調用到執行完成就對應一個棧幀在虛擬機棧中入棧到出棧的過程。
- 包含:局部變量表、棧操作數、動態鏈接、方法出口。存儲了方法調用的所有內容。(Stack Overflow 異常)。
- 本地方法棧
- 作用:專門爲native方法服務的。
- 也是由棧幀組成的。
-
方法區
1.作用:存儲被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的數據,編譯期需要保存的信息。 -
堆區
- 作用:所有通過new創建的對象的內存都在堆中分配。
- 特點:是虛擬機中最大的一塊內存,是GC要回收的部分。
- 堆區對內存的分配:
- 垃圾收集算法:確定哪些數據屬於垃圾
- 引用計數算法:最早使用的算法,1.2之前。對象通過引用的數量來確定是否能被GC回收。
缺陷:A引用B,B引用A,但是A,B都是不可達的。沒有路徑指向這兩個對象。所以他們已經是垃圾了,但是在此算法的場景下,這兩個對象並不能被JVM回收。
- 可達性算法:從GC Roots作爲起點開始搜索,那麼整個連通圖中的對象便都是活對象,對於GC Roots無法到達的對象便成了垃圾回收的對象,隨時可被GC回收。
ObjF,ObjD,ObjE都是垃圾。
- 引用的分類,帶星號的是常用的引用。
- *強引用:
- 軟引用:
- *弱引用:WeakReference
- 虛引用:
垃圾回收算法
標記-清楚算法:標記階段和清除階段構成。在標記階段會把所有的活動對象都做上標記,然後在清除階段會把沒有標記的對象,也就是非活動對象回收。
- 優點
- 實現簡單
- 與保守式 GC 算法兼容
- 缺點
- 碎片化嚴重(由上面描述的分配算法可知,容易產生大量小的分塊
- 分配速度慢(由於空閒區塊是用鏈表實現,分塊可能都不連續,每次分配都需要遍歷空閒鏈表,極端情況是需要遍歷整個鏈表的。
- 與寫時複製技術不兼容
複製算法:(Copying GC)是由Marvin L. Minsky在1963年研究出來的算法。原理是把內存分爲兩個空間一個是From空間,一個是To空間,對象一開始只在From空間分配,To空間是空閒的。GC時把存活的對象從From空間複製粘貼到To空間,之後把To空間變成新的From空間,原來的From空間變成To空間。
標記-整理算法:標記/整理算法與標記/清除算法非常相似,它也是分爲兩個階段:標記和整理。下面LZ給各位介紹一下這兩個階段都做了什麼。
標記:它的第一個階段與標記/清除算法是一模一樣的,均是遍歷GC Roots,然後將存活的對象標記。
整理:移動所有存活的對象,且按照內存地址次序依次排列,然後將末端內存地址以後的內存全部回收。因此,第二階段才稱爲整理階段。