日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JVM内部体系结构

發布時間:2025/3/15 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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.類加載過程:

(1) 裝載:查找和導入Class文件;(2) 鏈接:把類的二進制數據合并到JRE中;(a)校驗:檢查載入Class文件數據的正確性(文件格式驗證、元數據驗證、字節碼驗證(0x cafe bene)、符號引用驗證)(b)準備:給類的靜態變量分配存儲空間;(c)解析:將符號引用轉成直接引用;(3) 初始化:對類的靜態變量,靜態代碼塊執行初始化操作


驗證加載器加載順序:

在這里插入代碼片//測試加載器的加載順序 package java.lang; public class String {public static void main(String[] args) {//在類 java.lang.String 中找不到 main 方法System.out.println("hello world!");} }

上述代碼首先加載的是Bootstrap加載器,由于JVM中有java.lang.String這個類,所以會首先加載這個類,而不是自己寫的類,而這個類中并無main方法,所以會報“在類 java.lang.String 中找不到 main 方法”。
這個問題就涉及到,如果有兩個相同的類,那么java到底會用哪個?如果使用用戶自己定義的java.lang.String,那么別使用這個類的程序會去全部出錯,所以,為了保證用戶寫的源代碼不污染java出廠自帶的源代碼,而提供了一種“雙親委派”機制,保證“沙箱安全”。即先找到先使用。沙箱安全機制。

2.雙親委派機制:
如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把這個請求任務委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。雙親委派模型要求除了頂層的啟動類加載器外,其余的加載器都應該有自己的父類加載器。如果沒有雙親委任機制,不能保證Object類的唯一性(基礎類的統一性問題,上述代碼所展示的),那么系統中會存在多種不同的Object類。

運行時數據區

  • Native Interface && Native Method Stack
  • 主要用于調用JAVA無法完成的操作函數,需要借助c/c++等調用,現在比較少用

    在Thread類中竟然有一個只有聲明沒有實現的方法,并使用native關鍵字。用native表示,也此方法是系統級(底層操作系統或第三方C語言)的,而不是語言級的,java并不能對其進行操作,native方法不歸java管。native方法裝載在native method stack中。

    public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionality added* to this method in the future may have to also be added to the VM.** A zero status value corresponds to state "NEW".*/if (threadStatus != 0)throw new IllegalThreadStateException();/* Notify the group that this thread is about to be started* so that it can be added to the group's list of threads* and the group's unstarted count can be decremented. */group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}}private native void start0();
  • PC寄存器(Program Counter Register)

    說白了就是記錄下一個要運行指令的地址指針

  • 方法區(Method Area)
    方法區是供進程共享的運行時內存區域,他儲存了每一個類的結構信息(模板),如下圖。需要注意的是系列變量存在堆中,與方法區無關。


    永久代和元空間的解釋:

  • 方法區是一個規范,在不同的虛擬機里實現是不一樣的,類似于接口定義的規范,1.7落地是永久代,而1.8落地是元空間。 方法區邏輯上屬于堆的一部分,但是為了與堆進行區分,通常又叫“非堆”。


    4. Java 棧(Java Stack)
    先總結一句話:棧管運行,堆管存儲
    棧是在線程創建時創建,它的生命周期是隨線程的生命期的,線程結束棧內存也會釋放,是線程私有的。8中基本類型+對象的引用變量+實例方法都是在函數的占內存中分配的。
    注:8大基本類型

    棧幀的概念:java中的方法被扔進虛擬機的??臻g之后就成為“棧幀”,比如main方法,是程序的入口,被壓棧之后就成為棧幀。
    棧幀具體例子如下:

    以下代碼會因為不斷開辟棧幀,導致拋出StackOverflowError錯誤。

    public class Test{public static void m(){m();}public static void main(String[] args) {//Exception in thread "main" java.lang.StackOverflowErrorm();} } Exception in thread "main" java.lang.StackOverflowError


    棧+堆+方法區的關系如下:

  • 堆(heap)
  • 堆的物理分區=新生區+養老區,如下圖:

    堆的邏輯分區=新生區+養老區+永久代(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及JVM調優
    OOM產生的原因:
  • 新生區是類的誕生、成長、消亡的區域,一個類在這里產生,應用,最后被垃圾回收器收集,結束生命。 新生區又分為兩部分:伊甸區(Edenspace)和幸存者區(survivorpace),所有的類都是在伊甸區被new出來的。 幸存區有兩個:0區(survivor0space)1區(survivor1space)。 當伊甸園的空間用完時,程序又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收(MinorGC), 將伊甸園區中的不再被其他對象所引用的對象進行銷毀。然后將伊甸園中的剩余對象 移動到幸存0區。若幸存0區也滿了,再對該區進行垃圾回收,然后移動到1區。那如果1區也滿了呢?再移動到養老區。 若養老區也滿了,那么這個時候將產生Maj“GC(FuIIGC),進行養老區的內存清理。 若養老區執行了FullGC之后發現依然無法進行對象的保存,就會產生OOM異常"OutOfMemorError".

    產生OOM的代碼:

    public class OOM {public static void main(String[] args) {String str = "OOM";while (true){str += str + "OutOfMemoryError";//在堆中不停的實例化對象}} }

    如果出現·Java.lang.OutOfMemorError:Javaheapspace異常,說明java虛擬
    機的堆內存不夠。原因:

    1)java虛擬機的堆內存設置不夠,可以通過參數一xms、-xmx來調整。 (2)代碼中創建了大量大對象,并且長時間不能被垃圾收集器收集(存在被引用)。

    解決方法:可以通過IDEA調優,這里為了看到OOM的效果,故意調低了-Xms和-Xmx
    有一點需要強調:在實際生產中,xms和xmx一定要設置相等,可以防止內存頻繁波動造成神奇的bug

    下圖可以看出,Head堆物理內存確確實實是新生區+養老區(兩者相加內存=xms=xmx)

    詳細的new->GC->報OOM的過程

    詳細JVM調優

    總結

    以上是生活随笔為你收集整理的JVM内部体系结构的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。