JVM内部体系结构
JVM(Java Virtual Machine,Java虛擬機)
JVM是JRE的一部分。它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。JVM有自己完善的硬件架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。Java語言最重要的特點就是跨平臺運行。使用JVM就是為了支持與操作系統無關,實現跨平臺。所以,JAVA虛擬機JVM是屬于JRE的,而現在我們安裝JDK時也附帶安裝了JRE(當然也可以單獨安裝JRE)。
JVM體系:
JVM的內部體系結構分為三部分,分別是:類裝載器(ClassLoader)子系統,運行時數據區,和執行引擎
體系結構圖:
類加載器(Class loader)
Class loader有多種,可以說三個,也可以說是四個(第四個為自己定義的加載器,繼承 ClassLoader),系統自帶的三個分別為:
1.啟動類加載器(Bootstrap) ,C++所寫 2.擴展類加載器(Extension) ,Java所寫 3.應用程序類加載器(AppClassLoader)自己new的時候創建的是應用程序類加載器(AppClassLoader)。- Bootstrap: 是虛擬機自身的一部分,它負責將
<JAVA_HOME>/lib路徑下的核心類庫或-Xbootclasspath參數指定的路徑下的jar包加載到內存中,注意必由于虛擬機是按照文件名識別加載jar包的,如rt.jar,如果文件名不被虛擬機識別,即使把jar包丟到lib目錄下也是沒有作用的(出于安全考慮,Bootstrap啟動類加載器只加載包名為java、javax、sun等開頭的類)。 - Extension: 加載器是指Sun公司(已被Oracle收購)實現的sun.misc.Launcher$ExtClassLoader類,由Java語言實現的,是Launcher的靜態內部類,它負責加載<JAVA_HOME>/lib/ext目錄下或者由系統變量-Djava.ext.dir指定位路徑中的類庫,開發者可以直接使用標準擴展類加載器。
- AppClassLoader: 也稱應用程序加載器是指 Sun公司實現的sun.misc.Launcher$AppClassLoader。它負責加載系統類路徑java-classpath或-Djava.class.path指定路徑下的類庫,也就是我們經常用到的classpath路徑,開發者可以直接使用系統類加載器,一般情況下該類加載是程序中默認的類加載器,通過ClassLoader#getSystemClassLoader()方法可以獲取到該類加載器。
- 有需要時我們也可以通過繼承繼承Java. lang. ClassLoader來自定義自己的類加載器。
代碼驗證:
public class TestClassLoader {//Test:查看類加載器public static void main(String[] args) {Object object = new Object();//查看是那個“ClassLoader”加載Object類System.out.println(object.getClass().getClassLoader());//查看Object的加載器的上一層// error Exception in thread "main" java.lang.NullPointerException(已經是祖先了)//System.out.println(object.getClass().getClassLoader().getParent());System.out.println("-----------------------------------");TestClassLoader test = new TestClassLoader();System.out.println(test.getClass().getClassLoader().getParent().getParent());System.out.println(test.getClass().getClassLoader().getParent());System.out.println(test.getClass().getClassLoader());} } 輸出: null ----------------------------------- null sun.misc.Launcher$ExtClassLoader@1b6d3586 sun.misc.Launcher$AppClassLoader@18b4aac2類加載機制
參考文章
1.類加載過程:
驗證加載器加載順序:
上述代碼首先加載的是Bootstrap加載器,由于JVM中有java.lang.String這個類,所以會首先加載這個類,而不是自己寫的類,而這個類中并無main方法,所以會報“在類 java.lang.String 中找不到 main 方法”。
這個問題就涉及到,如果有兩個相同的類,那么java到底會用哪個?如果使用用戶自己定義的java.lang.String,那么別使用這個類的程序會去全部出錯,所以,為了保證用戶寫的源代碼不污染java出廠自帶的源代碼,而提供了一種“雙親委派”機制,保證“沙箱安全”。即先找到先使用。沙箱安全機制。
2.雙親委派機制:
如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把這個請求任務委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。雙親委派模型要求除了頂層的啟動類加載器外,其余的加載器都應該有自己的父類加載器。如果沒有雙親委任機制,不能保證Object類的唯一性(基礎類的統一性問題,上述代碼所展示的),那么系統中會存在多種不同的Object類。
運行時數據區
主要用于調用JAVA無法完成的操作函數,需要借助c/c++等調用,現在比較少用
在Thread類中竟然有一個只有聲明沒有實現的方法,并使用native關鍵字。用native表示,也此方法是系統級(底層操作系統或第三方C語言)的,而不是語言級的,java并不能對其進行操作,native方法不歸java管。native方法裝載在native method stack中。
PC寄存器(Program Counter Register)
說白了就是記錄下一個要運行指令的地址指針
方法區(Method Area)
方法區是供進程共享的運行時內存區域,他儲存了每一個類的結構信息(模板),如下圖。需要注意的是系列變量存在堆中,與方法區無關。
永久代和元空間的解釋:
4. Java 棧(Java Stack)
先總結一句話:棧管運行,堆管存儲
棧是在線程創建時創建,它的生命周期是隨線程的生命期的,線程結束棧內存也會釋放,是線程私有的。8中基本類型+對象的引用變量+實例方法都是在函數的占內存中分配的。
注:8大基本類型
棧幀的概念:java中的方法被扔進虛擬機的棧空間之后就成為“棧幀”,比如main方法,是程序的入口,被壓棧之后就成為棧幀。
棧幀具體例子如下:
以下代碼會因為不斷開辟棧幀,導致拋出StackOverflowError錯誤。
棧+堆+方法區的關系如下:
堆的物理分區=新生區+養老區,如下圖:
堆的邏輯分區=新生區+養老區+永久代(JDK1.7)JDK1.8之后永久區被替代成元數據,如下圖:
元空間和永久代之間最大的區別在于:永久代使用的是JVM的堆內存,而JDK1.8之后的元空間并不在虛擬機中,而是使用本機的物理內存。
當我們執行new Person()時,其實是new在新生區的伊甸園區,然后往下走,走到養老區,但是并未到元空間(元空間/永久代主要保存的是JDK自身所攜帶的Class,Interface的元數據(rt包),jar包等,不會被垃圾回收器回收,直到JVM關閉才會釋放此處內存)。
新生代GC(MinorGC過程(復制(復制算法)->清空->互換))
發生在新生代的垃圾收集動作,因為大多數Java對象存活率都不高,所以Minor GC非常頻繁,一般回收速度也比較快。
1:eden、SurvivorFrom復制到SurvivorTo,年齡+1 首先,當Eden區滿的時候會觸發第一次GC,把還活著的對象拷貝到survivorFrom區,當Eden區再次觸發GC的時候會掃描Eden區和From區域,對這兩個區域進行垃圾回收,經過這次回收后還存活的對象,則直接復制到To區域(如果有對象的年齡己經達到了老年的標準,則賦值到老年代區),同時把這些對象的年齡+1 2:清空eden、SurvivorFrom 然后,清空Eden和survivorFrom中的對象,也即復制之后有交換,誰空誰是to區。 3:SurvivorTo和SurvivorFrom互換 最后,SurvivorTo和SurvivorFrom互換,原SurvivorTo成為GC時的SurvivorFrom區。部分對象會在From和To區域中復制來復制去,如此交換15次(由」VM參數MaxTenurinThreshold 決定,這個參數默認是15)最終如果還是存活,就存入到老年代。老年代GC(一般是由標記清除或者是標記清除與標記壓縮的混合實現)(Major GC/Full GC)
指發生在老年代的垃圾收集動作,出現了Major GC,經常會伴隨至少一次的Minor GC(但并不是絕對的)。Major GC的速度一般要比Minor GC慢上10倍以上。
GC詳解
OOM產生的原因:
產生OOM的代碼:
public class OOM {public static void main(String[] args) {String str = "OOM";while (true){str += str + "OutOfMemoryError";//在堆中不停的實例化對象}} }如果出現·Java.lang.OutOfMemorError:Javaheapspace異常,說明java虛擬
機的堆內存不夠。原因:
解決方法:可以通過IDEA調優,這里為了看到OOM的效果,故意調低了-Xms和-Xmx
有一點需要強調:在實際生產中,xms和xmx一定要設置相等,可以防止內存頻繁波動造成神奇的bug
下圖可以看出,Head堆物理內存確確實實是新生區+養老區(兩者相加內存=xms=xmx)
詳細的new->GC->報OOM的過程
詳細JVM調優
總結
- 上一篇: 自平衡二叉树(Self-balancin
- 下一篇: Java8新特性之函数式接口