Java虚拟机:JVM 主要组成部分与内存区域
一、JVM 主要組成部分:
?JVM包含兩個子系統(tǒng)和兩個組件,分別為:
- Class loader(類裝載子系統(tǒng)):根據(jù)給定的全限定名類名來裝載class文件到運行時數(shù)據(jù)區(qū)的方法區(qū)中
- Execution engine(執(zhí)行引擎子系統(tǒng)):執(zhí)行引擎也叫解釋器,負責解釋class的指令,在提交給操作系統(tǒng)執(zhí)行
- Runtime data area(運行時數(shù)據(jù)區(qū)組件):即我們常說的JVM的內(nèi)存
- Native Interface(本地接口組件):與native lib交互,它的作用是融合不同的編程語言為Java所用,是其它編程語言交互的接口
????????首先通過編譯器把 Java源代碼轉(zhuǎn)換成字節(jié)碼,Class loader(類裝載)再把字節(jié)碼加載到內(nèi)存中,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),而字節(jié)碼文件只是 JVM 的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器執(zhí)行引擎(Execution Engine),將字節(jié)碼翻譯成底層系統(tǒng)指令,再交由 CPU 去執(zhí)行,而這個過程中需要調(diào)用其他語言的本地庫接口(Native Interface)來實現(xiàn)整個程序的功能。
二、JVM 內(nèi)存區(qū)域:
JVM 在執(zhí)行 Java 程序時,會將內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,不同的區(qū)域用途不同,創(chuàng)建和銷毀時間也不相同。在 JDK1.8 版本之后對運行時數(shù)據(jù)區(qū)域做了些修改,下面我們分別來看看修改前后的內(nèi)存區(qū)域是怎么樣的。
1、JDK1.8之前的JVM內(nèi)存區(qū)域如下圖:
2、JDK8之后的JVM內(nèi)存區(qū)域如下圖:
3、各區(qū)域的的作用:
(1)程序計數(shù)器:
當前線程執(zhí)行的字節(jié)碼的行號指示器,記錄當前線程執(zhí)行到程序的哪個位置,通過改變計數(shù)器的值,可以選取下一條需要執(zhí)行的字節(jié)碼指令。該區(qū)域是線程私有,是唯一一個不會發(fā)生OOM的區(qū)域。
(2)Java虛擬機棧:
描述 Java 方法執(zhí)行的內(nèi)存模型,每個方法執(zhí)行時都會創(chuàng)建一個棧幀,用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每個方法從調(diào)用到執(zhí)行完成,就對應(yīng)著一個棧幀在虛擬機中入棧到出棧的過程。該區(qū)域線程私有,生命周期與線程的生命周期相同。
(3)本地方法棧:
本地方法棧的作用和虛擬機棧的作用非常相似,區(qū)別是本地方法棧則為Native方法服務(wù),而虛擬機棧為執(zhí)行java方法服務(wù),該區(qū)域也是線程私有。
(4)Java堆:
用于存儲對象實例,是占用內(nèi)存最大的區(qū)域,可劃分為新生代和老年代,新生代又可細分為?Eden區(qū)、From Survivor區(qū)、To Survivor區(qū)。
在 HotSpot 中,對象在堆內(nèi)存布局分成三部分:對象頭,實例數(shù)據(jù),對齊填充。
① 對象頭:包括兩部分的信息:
- 運行時數(shù)據(jù):用于存儲對象自身的運行時數(shù)據(jù),如哈希碼,GC代年齡,鎖狀態(tài)標志、線程持有的鎖、偏向線程ID等。
- 類型指針:即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。如果對象是一個Java數(shù)組,那對象頭中還必須有一塊用于記錄數(shù)組長度的數(shù)據(jù)。
② 實例數(shù)據(jù):是對象真正存儲的有效信息,是在程序代碼中所定義的各種類型的字段內(nèi)容,相同寬度的字段會被分配到一起。
③ 對齊填充:并不是必然存在的,僅起著占位符的作用。
(5)方法區(qū):
????????用于存儲類信息,包括運行時常量池、靜態(tài)變量、常量、即時編譯后的代碼(即class文件)等數(shù)據(jù)。與Java堆一樣不需要連續(xù)的內(nèi)存,并且可以動態(tài)擴展,動態(tài)擴展失敗會拋出 OOM 異常,該區(qū)域被所有線程共享。對這塊區(qū)域進行垃圾回收的主要目標是對常量池的回收和對類型的卸載,但是一般比較難實現(xiàn)。
? ? ? ? 方法區(qū)是一個 JVM 規(guī)范,永久代與元空間都是其一種實現(xiàn)方式。JDK8 之前,Hotspot 中方法區(qū)的實現(xiàn)是永久代(Perm),JDK8 開始使用元空間(Metaspace),以前永久代的靜態(tài)變量和常量池移至堆內(nèi)存,其他內(nèi)容移至元空間,元空間直接在本地內(nèi)存分配。那為什么要使用元空間取代永久代的實現(xiàn)?主要是為了方便管理方法區(qū):
① 永久代的方法區(qū),和堆使用的物理內(nèi)存是連續(xù)的。對于永久代,由于類及方法的信息等比較難確定其大小,所以指定永久代的大小比較困難,太小容易出現(xiàn)永久代溢出,太大則容易導致老年代溢出,并且每次?Full GC 之后永久代的大小都會改變,如果動態(tài)生成很多 class 的話,就很可能出現(xiàn) OOM,畢竟永久代的空間配置有限。
②?JDK8之后,方法區(qū)存在于元空間,物理內(nèi)存不再與堆連續(xù),而是直接存在于本地內(nèi)存中,理論上機器內(nèi)存有多大,元空間就有多大。
③ 字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出。
④ 永久代會為 GC 帶來不必要的復雜度,并且回收效率偏低
參考文章:Java內(nèi)存區(qū)域(運行時數(shù)據(jù)區(qū)域)和內(nèi)存模型(JMM) - czwbig - 博客園
總結(jié)
以上是生活随笔為你收集整理的Java虚拟机:JVM 主要组成部分与内存区域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java虚拟机:class类文件结构
- 下一篇: Java基础篇:抽象类与接口