【JVM】體系結構及其細節

xd會飛的貓發表於2020-05-29

JVM

JVM執行在作業系統之上,與硬體沒有直接的互動。引入Java語言虛擬機器後,Java語言在不同平臺上執行時不需要重新編譯。Java語言使用Java虛擬機器遮蔽了與具體平臺相關的資訊,使得Java語言編譯程式只需生成在Java虛擬機器上執行的目的碼(位元組碼),就可以在多種平臺上不加修改地執行。

JVM的體系結構

Java棧、本地方法棧和程式計數器是執行緒私有的,記憶體佔用少,幾乎不存在垃圾回收。

方法區和堆所有執行緒共享,存在大量垃圾回收。

類裝載器

負責載入class檔案,class檔案在檔案開頭有特定的檔案標識(cafe babe),將class檔案載入到記憶體中,並將這些內容轉換成方法區中執行時資料機構,並且ClassLoader只負責class檔案的載入,執行由執行引擎負責。

 

虛擬機器自帶的載入器:

  • 啟動類載入器BootStrap【C++】
  • 擴充套件類載入器Extension【Java】
  • 應用程式類載入器AppClassLoader(系統類載入器):載入當前應用的classpath的所有類

使用者自定義的載入器:java.lang.ClassLoader的子類

 1     public static void main(String[] args) {
 2         Object object = new Object();
 3         //列印null
 4         System.out.println(object.getClass().getClassLoader());//jdk自帶的類--BootStrap
 5 
 6         MyObject myObject = new MyObject();
 7         //sun.misc.Launcher$AppClassLoader@18b4aac2
 8         System.out.println(myObject.getClass().getClassLoader());//自定義的類--AppClassLoader
 9         //sun.misc.Launcher$ExtClassLoader@4554617c
10         System.out.println(myObject.getClass().getClassLoader().getParent());
11         //null
12         System.out.println(myObject.getClass().getClassLoader().getParent().getParent());
13     }

sun.misc.Launcher:JVM相關呼叫的入口程式

雙親委派機制

當一個類受到類載入請求,不會自己去載入,而是委派給父類完成,所有的載入請求都應該傳送到啟動類載入器中,只有當父類載入器反饋自己無法完成請求的時候(載入路徑下沒有需要載入的Class),子類載入器才會嘗試自己去載入。

雙親委派機制的好處:

  • 防止重複載入同一個.class,通過委派給父類載入器,載入過了,就不用再載入一遍。保證資料安全。
  • 保證核心.class不能被篡改。通過委派,不會去篡改核心.clas,即使篡改也不會去載入,即使載入也不會是同一個.class物件了。不同的載入器載入同一個.class也不是同一個Class物件。這樣保證了Class執行安全。【防止汙染jdk原始碼】

沙箱安全機制

Java安全模型的核心就是Java沙箱,沙箱是一個限制程式執行的環境。沙箱機制就是將 Java 程式碼限定在虛擬機器(JVM)特定的執行範圍中,並且嚴格限制程式碼對本地系統資源訪問,通過這樣的措施來保證對程式碼的有效隔離,防止對本地系統造成破壞。沙箱主要限制系統資源訪問,系統資源包括CPU、記憶體、檔案系統、網路。不同級別的沙箱對這些資源訪問的限制也可以不一樣。所有的Java程式執行都可以指定沙箱,可以定製安全策略。

native介面和方法

普通方法放在Java棧中,native方法放在本地方法棧中。native是一個關鍵字,native方法在原始碼中只有宣告,沒有實現。

本地介面:為了融合其他程式語言,需要呼叫c/c++程式的時候,在記憶體中開闢一塊區域處理native程式碼,在執行引擎執行的時候載入本地庫。【目前使用越來越少,一般與硬體相關的應用會使用】

本地方法棧:在棧中登記native方法,在執行引擎執行的啥時候載入本地方法庫。

程式計數器(PC暫存器)

用於記錄方法之間的呼叫和執行情況。每個執行緒都有一個程式計數器,執行緒私有,就是一個指向方法區中的方法位元組碼的指標,用來儲存指向下一條指令的地址),也即將要指向的指令程式碼,由執行引擎來讀取下一條指令,是一個非常小的記憶體空間,幾乎可以忽略不計。

實際上就是當前執行緒所指向的位元組碼的行號指示器,位元組碼直譯器通過改變計數器的值來選取下一條需要執行的位元組碼指令,如果執行的是native方法,則計數器是空的。用來完成分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能,不會發生記憶體溢位(OOM)錯誤。

方法區

方法區是執行緒共享的執行時記憶體區域,它儲存每個類的結構資訊。例如:常量池、欄位、方法資料、建構函式、普通方法的位元組碼內容。【方法區是一個規範,不同的虛擬機器中有著不一樣的實現,例如永久代和元空間】

例項變數存在堆記憶體中,和方法區無關

Java棧

棧負責執行,堆負責儲存。

Java棧負責程式的執行,線上程建立的時候建立,生命週期跟隨執行緒的生命週期,是執行緒私有的,執行緒結束,記憶體釋放。Java棧不存在垃圾回收的問題,執行緒一結束,棧記憶體就釋放了。

Java的8種基本型別,物件的引用變數和例項方法都是在函式的棧記憶體種分類。

棧記憶體儲的資料:

  • 本地變數:輸入、輸出引數及方法內的變數(區域性變數)
  • 棧操作:出棧、入棧的操作
  • 棧幀資料:類檔案、方法等

什麼是棧幀?參考 ☞ https://www.jianshu.com/p/b666213cdd8a

Person p = new Person()

引用變數p存在棧記憶體中  例項變數new Person()存在堆記憶體中

棧的執行原理:棧中的資料已棧幀的格式存在,棧幀是一個記憶體區塊,是一個關於方法和執行期資料的資料集。其大小和JVM的實現有關,通常在256K~756K之間,1Mb左右

當方法A被呼叫是產生一個棧幀F1被壓入棧中,A呼叫了B,B的棧幀F2被壓入棧,B呼叫C,C的棧幀F3被壓入棧幀。執行完畢後,F3、F2和F1一次彈出。

棧異常:java.lang.StackOverflowError (SOF)屬於錯誤

 

堆、棧和方法區之間的關係

 

 HotSpot是使用指標的方式來訪問物件【Java HotSpot補充:https://www.jianshu.com/p/714eb5adadb9

Java堆會存放訪問類後設資料(類的結構資訊)的地址

reference儲存是物件地址

Java堆中可以存在多個Person例項:p1、p2、p3。而p1,p2,p3都指向方法區中同一個物件型別資料(Person Class),即類的結構資訊。

相關文章