讓你秒懂JVM底層
- 一、JVM體系結構
- 二、深入探討JVM核心模塊
- 1、類加載器
- 2、運行時資料區
- 3、執行引擎
- 三、詳細結構圖
- 四、未來作業
一、JVM體系結構
在學習JVM之前,我們需要知道JVM虛擬機有哪些種類?目前比較主流的有HotSpot VM、J9 VM、Zing VM、JRockit VM等等,今天我們要講的也就是大家普遍用的HotSpot VM,大家可以通過命令去查看我們使用的是什么種類的虛擬機,

首先我們先從整體上來看一下JVM主要的組成成分,我整理了一張比較適合記憶的JVM架構圖,這張圖簡單易畫,并且囊括了JVM重要的部分,幾乎能解決80%關于JVM分析的問題,

通過這張圖我們可以發現JVM被分為三大核心模塊:
- 類加載器(Class Loader)
- 運行時資料區(Runtime Data Area)
- 執行引擎(Execution Engine)
接下來我就詳細點出比較重要且面試必問的幾個點,
二、深入探討JVM核心模塊
1、類加載器
??首先我們要知道類加載器的作用:其實類加載器就是將class檔案位元組碼內容加載到記憶體中,并將這些靜態資料轉換成方法區的運行時資料結構,然 后在堆中生成一個代表這個類的java.lang.Class物件,作為方法區中類資料的訪問入口,說白了就是加載類,將Java類加載到Java虛擬機上去并執行,
??其次我們還需要認識一個名詞叫類快取:就是在標準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(快取)一段時間,不過JVM垃圾回識訓制可以回收這些Class物件,換句話說就是類在被識訓之前,只會被加載一次,這樣就不需要反復加載同一個類了,
??類加載的程序主要分為加載、連接、初始化三大步,連接又可細分為驗證、準備和決議三步,如下圖,

每一步主要的任務:
- 加載:通過路徑名找到這個要加載的類,將它加載到JVM中,這是類加載的入口,
- 驗證:對這個類的位元組碼格式進行驗證,驗證是否符合被加載的要求
- 準備:為類加載創造條件,比如為類的靜態變數分配記憶體等等
- 決議:將符號參考替換成直接參考
- 初始化:對靜態變數,靜態代碼塊執行初始化作業
在Java中一共有四種類加載器:
1、引導類加載器(bootstrap class loader)
JVM自帶的類加載器,負責java平臺的核心庫,并且這個類加載器使用C++實作的,不能直接獲取,
2、擴展類加載器(extensions class loader)
它用來加載 Java 的擴展庫,Java 虛擬機的實作會提供一個擴展庫目錄,該類加載器在此目錄里面查找并加載 Java 類,
3、應用程式類加載器(application class loader)
根據Java應用的類路徑來加載Java類的,一般來說Java應用的類都是它加載的,
4、用戶自定義類加載器
程式員可以根據自己的需求繼承java.lang.ClassLoader類的方式實作自己的類加載器,
??這么多類加載器那JVM怎么知道選擇哪一個類加載器來加載我們的類呢?這就涉及到類加載程序一個重要的機制雙親委派機制,說白了就是往上找爹,當一個類加載器收到類加載的請求時,這個類加載器會將這個請求委托給父加載器去完成,一直向上委托,直到引導類加載器;引導類加載器會去判斷,我是否能加載這個類,如果能,就加載這個類結束,否則就通知子加載器去加載,直至加載成功結束,如下圖,
面試題:
- 雙親委派機制有什么作用呢?
- 主要是為了安全性,防止用戶自己寫的Java類替換了Java核心類,如java.lang.String等等
- 也能防止同一個類被多個加載器加載,一旦父加載器加載,子加載器就沒必要再次加載了,
- 雙親委派機制能被打破嗎?,如果能,如何打破?
??答:可以被打破,要想打破雙親委派機制首先需要自己寫一個類繼承ClassLoader類,然后重寫里面的loadClass和findClass方法,
2、運行時資料區
??運行時資料區如文章開頭那張圖上畫的一樣,主要有五部分:
- Java堆:堆是一個不連續的記憶體空間,分配靈活,速度慢!因此堆主要存盤Java的所有物件及其對應的實體變數和陣列(其實陣列也是物件),另外每個JVM只有一個堆區域,
- 方法區:其實方法區也是特殊的堆,每個JVM也只有一個方法區,該區域主要存盤靜態變數、類和常量相關的資訊等等,
因為Java堆和方法區都只有一個,因此這兩部分都是被所有執行緒共享的,所存盤的資料不是執行緒安全的,
- Java堆疊:由系統自動分配的,速度快,是一個連續的記憶體空間,主要存盤8大基本型別、物件的參考和實體的方法,對于每個執行緒,將會創建單獨的運行時堆疊存放該執行緒執行方法的資訊(實際引數、區域變數等);對于每個方法被呼叫都會創建一個堆疊幀(存盤區域變數、運算元、方法出口等),因此堆疊里面幾乎不會存在垃圾,
- 在面試里,堆疊幀也是一個經常被問的一個點!!!我在下圖的堆疊中簡單畫了兩個堆疊幀,幫助大家理解一下,

- PC程式計數器:它和堆疊相似每個執行緒都有單獨的PC計數器,用于存盤指向下一條指令的地址,也就是即將要執行的指令代碼,
- 本地方法堆疊:本地方法堆疊用于管理本地方法的呼叫
對于以上三個部分,每個執行緒都將創建一個單獨的組件,因此這三部分都是執行緒獨占的,不共享資源,所存盤的資料是執行緒安全的,
??現在我們通過一個簡單的例子具體去看一下JVM記憶體主要存盤的資料有哪些
package com.clazz;
/**
* @author xsl
*/
public class Person
{
static {
System.out.println("Person類的靜態代碼塊");
}
private String name;
private int age;
private String sex="男生";
public void study(){
String k = "區域變數";
System.out.println("我在學習,,,");
}
public static void main(String[] args)
{
Person p1 = new Person();
Person p2 = new Person();
}
}

3、執行引擎
??Java之所以可以跨平臺,和JVM的執行引擎有密不可分的關系,執行引擎的任務主要就是把位元組碼指令解釋/編譯為對應平臺上的本地機器指令,說白了,執行引擎就是充當了一個翻譯官,
??它主要包含解釋器、JIT編譯器兩部分,
- 解釋器(Interpreter):當Java虛擬機啟動時會根據預定義的規范對位元組碼采用逐行解釋的方式執行,將每條位元組碼檔案中的內容“翻譯”為對應平臺的本地機器指令執行,
- JIT編譯器(JIT Compiler):就是把源代碼編譯成機器語言,相信學過編譯原理的小伙伴都知道編譯的六大步驟,
三、詳細結構圖
最后附上JVM詳細的結構圖,相信有了上述的知識儲備,再來理解官方給的圖就很容易了,

四、未來作業
??通過上面的介紹,想必許多小伙伴已經對JVM結構模型有一定的了解了,但是對于JVM的所有內容,上述知識只是冰山一角,比如我們在存盤資料時,堆疊肯定不是無限大的,那肯定需要對堆疊里面的垃圾進行回收,這就涉及到JVM的垃圾回收GC;當有些垃圾沒有及時被處理就會導致堆疊記憶體溢位,所以什么時候會發生堆疊溢位?堆疊溢位怎么辦?既然JVM存在一些不足,我們是否需要對JVM進行優化呢?這就涉及到JVM的調優,那如何進行調優呢?等等這一系列問題都是需要我們去深入研究的,對于這些問題,我后面也會寫一些文章詳細去剖析解決這些問題的,希望大家多多支持,最后如果有不足或錯誤的地方,歡迎大家指正,您的點贊和評論是我前進的動力,謝謝大家,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/283040.html
標籤:其他
上一篇:Spring Boot Admin的使用(Actuator監控介面)
下一篇:淺談bpduguard安全特性
