更新時間:2020-09-30 15:32:42 來源:動力節(jié)點 瀏覽2444次
事實上,每個Java程序都離不開Java虛擬機,Java程序的運行依靠具體的Java虛擬機實例。JVM在Java的程序運行中起到了關(guān)鍵性的作用,JVM內(nèi)存結(jié)構(gòu)為JVM在Java程序運行中取得了不可忽略的地位。
JVM在Java程序運行時把它所管理的內(nèi)存劃分為幾個不同的數(shù)據(jù)區(qū)域:程序計數(shù)器(Program Counter Register)、虛擬機棧(VM Stack)、本地方法棧(Native Method Stack)、 方法區(qū)(Method Area)、堆(Heap)。
JVM內(nèi)存結(jié)構(gòu)如下圖所示:


如上圖所示,方法區(qū)和堆為線程共享區(qū),虛擬機棧、本地方法棧和程序計數(shù)器為線程獨占區(qū)。方法區(qū)則是虛擬機規(guī)范中對運行時數(shù)據(jù)區(qū)劃分的一個內(nèi)存區(qū)域,不同的虛擬機廠商可以有不同的實現(xiàn),而HotSpot虛擬機以永久代來實現(xiàn)方法區(qū),所以方法區(qū)是一個規(guī)范,而永久代則是其中的一種實現(xiàn)方式。
程序計數(shù)器(Program Counter Register),也有稱作為PC寄存器。程序計數(shù)器是一塊較小 的空間,它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。
如果線程執(zhí)行的是java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址,如果正在執(zhí)行的是native方法,這個計數(shù)器的值為undefined。
JVM的多線程是通過線程輪流切換并分配CPU執(zhí)行時間片的方式來實現(xiàn)的,任何一個時刻,一個CPU都只會執(zhí)行一條線程中的指令。為了保證線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各線程間的程序計數(shù)器獨立存儲,互不影響。
此區(qū)域是唯一一個在java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域,因為程序計數(shù)器是由虛擬機內(nèi)部維護的,不需要開發(fā)者進行操作。
描述的是java 方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候 都會創(chuàng)建一個“棧幀”用于存儲局部變量表(包括參數(shù))、操作棧、方法出口等信息。每個方法被調(diào)用到執(zhí)行完的過程,就對應(yīng)著一個棧幀在虛擬機棧中從入棧到出棧的過程。聲明周期與線程相同,是線程私有的。
局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(引用指針,并非對象本身),其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量的空間,其余數(shù)據(jù)類型只占1個。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進入一個方法時,這個方法需要在棧幀中分配多大的局部變量是完全確定的,在運行期間棧幀不會改變局部變量表的大小空間。
與虛擬機?;绢愃?,區(qū)別在于虛擬機棧為虛擬機執(zhí)行的java方法服務(wù),而本地方法棧則是為Native方法服務(wù)。HotSpot虛擬機不區(qū)分虛擬機棧和本地方法棧,兩者是一塊的。與虛擬機棧一樣,本地方法棧也會拋StackOverflowError和OutOfMemoryError異常。
JVM管理的最大的一塊內(nèi)存區(qū)域,存放著對象的實例,是線程共享區(qū)。
堆是垃圾收集器管理的主要區(qū)域,因此也被稱為“GC堆”。
JAVA堆的分類:
從內(nèi)存回收的角度上看,可分為新生代(Eden空間,F(xiàn)rom Survivor空間、To Survivor空間)及老年代(Tenured Gen)
從內(nèi)存分配的角度上看,為了解決分配內(nèi)存時的線程安全性問題,線程共享的JAVA堆中可能劃分出多個線程私有的分配緩沖區(qū)(TLAB)
JAVA堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可。
可通過參數(shù) -Xmx -Xms 來指定運行時堆內(nèi)存的大小,堆內(nèi)存空間不足也會拋OutOfMemoryError異常。
也稱”永久代” 、“非堆”,它用于存儲虛擬機加載的類信息、常量、靜態(tài)變量、是各個線程共享的內(nèi)存區(qū)域。默認(rèn)最小值為16MB,最大值為64MB,可以通過-XX:PermSize 和 -XX:MaxPermSize 參數(shù)限制方法區(qū)的大小。
運行時常量池:是方法區(qū)的一部分,其中的主要內(nèi)容來自于JVM對Class的加載。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯器生成的各種符號引用,這部分內(nèi)容將在類加載后放到方法區(qū)的運行時常量池中。
直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域,它直接從操作系統(tǒng)中分配,因此不受Java堆大小的限制,但是會受到本機總內(nèi)存的大小及處理器尋址空間的限制,因此它也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。在JDK1.4中新引入了NIO機制,它是一種基于通道與緩沖區(qū)的新I/O方式,可以直接從操作系統(tǒng)中分配直接內(nèi)存,即在堆外分配內(nèi)存,這樣能在一些場景中提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)。
以上就是對JVM內(nèi)存結(jié)構(gòu)的全面解析,對JVM內(nèi)存結(jié)構(gòu)的每個部分都作了詳細(xì)的介紹,對我們?nèi)鎸W(xué)習(xí)和了解JVM內(nèi)存結(jié)構(gòu)會有很大的幫助。當(dāng)然,你也可以觀看本站的Java零基礎(chǔ)視頻教程,里面有更全面的JVM體系的知識,為你打開一扇Java知識大門!