1. 程式人生 > >司馬老峰的技術沙龍

司馬老峰的技術沙龍

JVM(Java Virtual Machine)

Java程式的一大特性就是跨平臺特性。Java虛擬機器(JVM)是實現這一特性的關鍵。因為位元組碼檔案(.class)可以在任何具有Java虛擬機器的計算機或者電子裝置上執行,Java虛擬機器中的Java直譯器負責將.class檔案解釋成為特定的機器碼進行執行。從而可以達到一次編譯到處執行的效果。這一特性就好比各個廠商的瀏覽器各不相同,但是html都能在這些瀏覽器上正常顯示。

JVM記憶體區域劃分

JVM 大致可以分為三部分,分別是:類載入器,執行時資料區和執行引擎。

類載入器

類載入器負責載入編譯好的.class位元組碼檔案,裝入記憶體,使JVM可以通過例項化或其它方式使用載入後的類。

類載入器分類

  1. 啟動類載入器(BootStrap Class Loader):負責載入rt.jar檔案中所有的Java類。這個類載入器是由C++實現的,並且無法被程式引用。
  2. 擴充套件類載入器(Extension Class Loader):負責載入<JAVA_HOME>/lib/ext下的類庫。
  3. 應用程式類載入器(Application Class Loader):負責載入啟動引數中指定的Classpath中的所有類庫。
  4. 使用者自定義類載入器(User Defined Class Loader):由使用者自定義類的載入規則,可以手動控制載入過程中的步驟。

類載入器工作原理

類的生命週期分為: 載入→連線(驗證→準備→解析)→初始化→使用→解除安裝,五個階段

載入階段

  1. 通過一個類的全限定名來獲取此類的二進位制位元組流;
  2. 將位元組流所代表的的靜態儲存結構轉化為方法區的執行時資料結構;
  3. 在記憶體中生成一個代表這個類的 java.lang.class 物件, 作為方法區這個類的各種資料的訪問入口。

一個類的二進位制位元組流可以從zip、網路和資料庫中獲取,也可以在執行時動態生成,也可以通過.java檔案以外的檔案生成(如jsp檔案)。

通常一個類的載入階段中的獲取二進位制位元組流的操作是可控的(通過實現自定義類載入器)。

驗證階段

驗證階段是為了確保.class檔案位元組流中包含的資訊符合當前虛擬機器的要求。

  1. 檔案格式驗證:保證輸入的位元組流能正確的解析並存儲於方法區內,格式上符合描述一個java型別資訊的要求。
    1. 是否以魔數0xCAFEBABE開頭
    2. 主次版本號是否在當前虛擬機器的處理範圍內
    3. 常量池的常量是否有不被支援的常量型別
  2. 元資料驗證:對位元組碼描述的資訊進行語義分析,確保符合java語言規範。
    1. 是否有父類
    2. 是否繼承了不允許被繼承的類
  3. 位元組碼驗證:通過資料流和控制流分析確定程式語義是合法的符合邏輯的。對類的方法體進行校驗分析,保證被校驗類的方法在執行時不會做出危害虛擬機器的行為
  4. 符號引用驗證:對類自身以外的資訊(常量池中的各種符號引用)進行匹配性校驗
    1. 引用中通過全限定名描述的類能否找到

通常如果我們執行的程式已經經過了嚴格的驗證階段,可以通過-Xverify:none 關閉驗證措施,縮短類載入的時間。

準備階段

正式為類變數分配記憶體並設定變數初始值。(初始值,不是程式在開發時設定的預設值,而是變數所屬型別的初始值如int初始值是0)

解析階段

將常量池內的符號引用替換為直接引用的過程

  1. 類或介面的解析:載入目標類,確認訪問許可權
  2. 欄位解析:解析欄位所屬類或介面的符號引用
  3. 類方法解析:解析出類方法所屬類或介面的符號引用
  4. 介面方法解析:解析出介面方法所屬類或介面的符號引用。

初始化階段

初始化類變數和其他資源,為類變數賦值(使用開發時設定的預設值)

執行時資料區

執行時資料區由方法區、堆、Java虛擬機器棧、程式計數器、本地方法棧組成。

程式計數器

當前執行緒所執行位元組碼的行號指示器,由於Java是支援多執行緒執行的,所以程式執行的軌跡不可能一直都是線性執行。當有多個執行緒交叉執行時,被中斷的執行緒的程式當前執行到哪條記憶體地址必然要儲存下來,以便用於被中斷的執行緒恢復執行時再按照被中斷時的指令地址繼續執行下去。為了執行緒切換後能恢復到正確的執行位置,每個執行緒都需要有一個獨立的程式計數器,各個執行緒之間計數器互不影響,獨立儲存,我們稱這類記憶體區域為“執行緒私有”的記憶體。

java 虛擬機器棧

java虛擬機器棧也是執行緒私有的,生命週期與執行緒相同。java虛擬機器棧描述的是java方法執行的記憶體模型,每個方法在執行的同時會建立一個棧幀用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。每個方法從呼叫到執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。

本地方法棧

與虛擬機器棧相似,不同的是虛擬機器棧為虛擬機器執行java方法服務,而本地方法棧則為虛擬機器使用到的Native方法服務。

java 堆

這是執行緒共享的記憶體區域,用於存放物件例項,幾乎所有的物件例項都在這裡分配記憶體。這裡也是java垃圾收集器管理的主要區域。從記憶體回收角度java堆還可分為新生代和老年代等等。

方法區

與java堆一樣,這裡也是各個執行緒共享的記憶體區域,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數等資料。

執行引擎

JVM執行java程式碼的方式主要有解釋執行(通過直譯器執行)、編譯執行(通過即時編譯器產生原生代碼執行)兩種方式