【JVM】體系結構及其細節

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),即類的結構信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章