談談JVM類加載機制與內(nèi)存模型

1、類加載機制

1.1、什么是類加載機制

我的理解是Java編譯器將java文件編譯成字節(jié)碼文件,然后類加載器將字節(jié)碼文件加載進JVM,放到對應內(nèi)存區(qū)域中

1.2、類加載的生命周期

類加載的生命周期是從類被加載到內(nèi)存開始,直到卸載出內(nèi)存為止的。整個生命周期分為7個階段:加載、驗證、準備、解析、初始化、使用、卸載。其中,驗證、準備、解析三部分統(tǒng)稱為連接。

  1. 加載:查找和導入Class文件;
  2. 連接:把類的二進制數(shù)據(jù)合并到JRE中;
    • 校驗:檢查載入Class文件數(shù)據(jù)的正確性;
    • 準備:給類的靜態(tài)變量分配存儲空間;
    • 解析:將符號引用轉(zhuǎn)成直接引用;
  3. 初始化:對類的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作。

1.3、類加載器

把類加載階段的“通過一個類的全限定名來獲取描述此類的二進制字節(jié)流”這個動作交給虛擬機之外的類加載器來完成。這樣的好處在于,我們可以自行實現(xiàn)類加載器來加載其他格式的類,只要是二進制字節(jié)流就行,這就大大增強了加載器靈活性。系統(tǒng)自帶的類加載器分為三種:

  1. 啟動類加載器。(BootStrap ClassLoader)
  2. 擴展類加載器。(Extension ClassLoader)
  3. 應用程序類加載器。(Application ClassLoader)

1.4、雙親委派機制

雙親委派

雙親委派這個詞聽起來比較高深,其實就是如字面意思所示,如果一個類加載器收到了類加載器的請求.它首先不會自己去嘗試加載這個類。而是把這個請求委派給父加載器去完成.每個層次的類加載器都是如此。因此所有的加載請求最終都會傳送到Bootstrap類加載器(啟動類加載器)中。只有父類加載反饋自己無法加載這個請求(它的搜索范圍中沒有找到所需的類)時。子加載器才會嘗試自己去加載。
雙親委派模型的優(yōu)點:java類隨著它的加載器一起具備了一種帶有優(yōu)先級的層次關系。


2、內(nèi)存模型

2.1、什么是Java內(nèi)存模型

Java內(nèi)存模型(Java Memory Model ,JMM)就是一種符合內(nèi)存模型規(guī)范的,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的,保證了Java程序在各種平臺下對內(nèi)存的訪問都能保證效果一致的機制及規(guī)范。

簡要言之,jmm是jvm的一種規(guī)范,定義了jvm的內(nèi)存模型。它屏蔽了各種硬件和操作系統(tǒng)的訪問差異,不像c那樣直接訪問硬件內(nèi)存,相對安全很多,它的主要目的是解決由于多線程通過共享內(nèi)存進行通信時,存在的本地內(nèi)存數(shù)據(jù)不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執(zhí)行等帶來的問題??梢员WC并發(fā)編程場景中的原子性、可見性和有序性。

根據(jù)java虛擬機規(guī)范,java虛擬機管理的內(nèi)存將分為下面五大區(qū)域。

2.2、五大內(nèi)存區(qū)域簡析

程序計數(shù)器

程序計數(shù)器是一塊很小的內(nèi)存空間,它是線程私有的,可以認作為當前線程的行號指示器。

注意:如果線程執(zhí)行的是個java方法,那么計數(shù)器記錄虛擬機字節(jié)碼指令的地址。如果為native【底層方法】,那么計數(shù)器為空。這塊內(nèi)存區(qū)域是虛擬機規(guī)范中唯一沒有OutOfMemoryError的區(qū)域。

Java棧(虛擬機棧)

同計數(shù)器也為線程私有,每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表,操作棧,動態(tài)鏈接,方法出口等信息。每一個方法被調(diào)用的過程就對應一個棧幀在虛擬機棧中從入棧到出棧的過程。

Java虛擬機??赡艹霈F(xiàn)兩種類型的異常:
1、線程請求的棧深度大于虛擬機允許的棧深度,將拋出StackOverflowError。
2、虛擬機??臻g可以動態(tài)擴展,當動態(tài)擴展是無法申請到足夠的空間時,拋出OutOfMemory異常。

本地方法棧

本地方法棧是與虛擬機棧發(fā)揮的作用十分相似,區(qū)別是虛擬機棧執(zhí)行的是Java方法(也就是字節(jié)碼)服務,而本地方法棧則為虛擬機使用到的native方法服務,可能底層調(diào)用的c或者c++,我們打開jdk安裝目錄可以看到也有很多用c編寫的文件,可能就是native方法所調(diào)用的c代碼。

對于大多數(shù)應用來說,堆是java虛擬機管理內(nèi)存最大的一塊內(nèi)存區(qū)域,因為堆存放的對象是線程共享的,所以多線程的時候也需要同步機制。

注意:它是所有線程共享的,它的目的是存放對象實例。同時它也是GC所管理的主要區(qū)域,因此常被稱為GC堆,又由于現(xiàn)在收集器常使用分代算法,Java堆中還可以細分為新生代和老年代,再細致點還有Eden(伊甸園)空間之類的不做深究。
根據(jù)虛擬機規(guī)范,Java堆可以存在物理上不連續(xù)的內(nèi)存空間,就像磁盤空間只要邏輯是連續(xù)的即可。它的內(nèi)存大小可以設為固定大小,也可以擴展。
當前主流的虛擬機如HotPot都能按擴展實現(xiàn)(通過設置 -Xmx和-Xms),如果堆中沒有內(nèi)存內(nèi)存完成實例分配,而且堆無法擴展將報OOM錯誤(OutOfMemoryError)

方法區(qū)

方法區(qū)同堆一樣,是所有線程共享的內(nèi)存區(qū)域,用于存儲已被虛擬機加載的類信息、常量(常量池)、靜態(tài)變量,如static修飾的變量加載類的時候就被加載到方法區(qū)中。

參考:
深入理解JVM-內(nèi)存模型(jmm)和GC

淺析JVM類加載機制

類加載機制-深入理解jvm

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容