詳解JVM內存結構模型

衆說周知,Java語言號稱跨平臺,一次編寫,到處運行,而這離不開Java虛擬機(JVM)的存在。

Java並非JVM的唯一語言選擇,Java虛擬機是定義了自己的JVM規定(字節碼文件.class),只要符合JVM規範便可在JVM上運行。如今也出現了其他一些可以在Java虛擬機上運行的語言,例如Kotlin、Scala。理解JVM內存模型,也能幫助我們更好的理解程序的運行。

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分爲若干個不同的數據區,不同的區域有着不同的作用,具體如下:

上圖中,灰色部分則是線程共享的數據區,白色的則爲線程隔離。各個區域的作用簡單可以總結成如下:

虛擬機棧 線程私有,生命週期跟線程相同,存儲局部變量表,操作數棧,動態鏈接以及方法的出口等信息。每一個方法從調用到執行接口,就對應於一個棧幀在虛擬機棧中的進棧和出棧
Java堆 線程共享,在虛擬機啓動時創建,存儲所有創建的對象實例。
方法區 線程共享,該區域跟堆很類似,存儲是已被加載的類信息,常量,靜態變量以及即時編譯後的代碼,常量池就在該區域。
本地方法棧 線程私有,爲虛擬機使用到的Native方法服務
程序計數器 線程私有,記錄程序當前運用的所在的行數

Java堆是JVM管理內存中最大的一塊區域,存放的是對象的實例而對象存在生命週期,因而爲了避免內存溢出,也伴隨着出現JVM對內存的分配和回收的問題。

內存的分配,虛擬機遇到new指令時,首先會去檢查該指令的參數能否在常量池找到這個類的符號引用,並且檢查這個類是否被加載、解析和初始化過。如果沒有,則先執行類加載過程。如果已被加載,則爲對象分配內存空間,分配的方式有兩種:

  • 指針碰撞,通過移動指針分配空閒空間,因此內存絕對規整
  • 如果已使用和空閒內存相互交錯,沒法使用指針碰撞,則採用空閒列表。JVM需要維護一個列表記錄哪些內存塊可用

常量池存在於方法區中,是方法區的一部分。存放編譯時期生產的各種字面量和符號引用。字面量接近於Java中的常量,而符號引用包括:類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符。JVM對方法區的回收主要就集中在對常量池的回收和對類的類型的卸載。

從內存回收的角度看,Java堆又分成新生代和老年代,新生代又分爲Eden空間,Survivor空間,兩者默認比例是8:1。對象創建時會默認存在Eden區域,新生代進行GC(垃圾回收)時候,會將存活的對象移動到Survivor空間,同時存活年齡+1,當對象的存活年齡大於15則會進入老年代。如果對象創建時太大,新生代觸發一次GC後對象放不下也會直接進入老年代。

分代的主要目的是爲了更好的內存回收和空間分配。年輕代中的存在許多生命週期比較短的對象,而老年代中的對象大部分生命週期較長,出於回收效率的考慮,JVM可以針對不同的空間執行不同的回收策略,像HotSpot對於新生代採用複製算法,老年代則採用標記清除算法。

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