運行時數(shù)據(jù)區(qū)域
Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存區(qū)域劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而存在,有些區(qū)域則依賴用戶線程的啟動和結(jié)束而建立和銷毀。
程序計數(shù)器
是一塊較小的內(nèi)存空間。它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。
由于Java多線程是通過線程輪流切換并分配處理器執(zhí)行時間來實現(xiàn)的。所以每條線程都有自己獨立的程序計數(shù)器,這樣多線程程序運行時才不會錯亂。所以也稱其為線程“私有”內(nèi)存。
需要注意的是;
- Java方法:如果當(dāng)前線程正在執(zhí)行的是Java方法。這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址。
- Native方法:如果執(zhí)行Native方法,則計數(shù)器記錄為空。所以是唯一一個不存在OutOfMemoryError情況的內(nèi)存區(qū)域。
虛擬機棧(重要)
- 線程私有,生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型,每個方法執(zhí)行都會創(chuàng)建一個棧幀。
- 棧幀(Stack Frame):用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。局部變量表存放編譯時的8中基本數(shù)據(jù)類型、引用類型和returnAddress類型(指向一條字節(jié)碼指令地址)。
- 該區(qū)域有兩種異常:
- 線程請求棧深度大于虛擬機棧深度,拋出StackOverflowError異常。
- 動態(tài)拓展時無法申請到足夠內(nèi)存,拋出OutOfMemoryError異常。
如果對棧幀所包含的概念不太清楚可以參考:
JAVA內(nèi)存結(jié)構(gòu)之運行時棧幀結(jié)構(gòu)
本地方法棧
與虛擬機棧功能類似,但虛擬機棧為Java方法服務(wù),而本地方法棧為Native方法服務(wù)。也有StackOverflowError和 OutOfMemoryError異常。
Java堆(重要)
- Java堆(Java Heap)是Java虛擬機管理的最大內(nèi)存區(qū)域,虛擬機啟動時創(chuàng)建,所有線程共享該內(nèi)存。該內(nèi)存唯一目的就是存放對象實例,幾乎所有的對象實例都在此分配內(nèi)存。
- Java堆是垃圾收集器管理的主要區(qū)域,也被成為“GC堆”。Java堆可被細分為:新生代和老年代。
- 新生代: 用來存放生命周期較短的對象,而新生代又使用復(fù)制算法進行GC ,又將其按照8:1:1的比例分為一塊較大的Eden空間和2個較小的From Survivor和To Survivor空間。
- 老年代:用來存放生命周期較長的對象。
- Java堆可以處于物理上不連續(xù)內(nèi)存區(qū)域,但需要邏輯上連續(xù)。
- 如果堆中沒有內(nèi)存進行實例分配,并且堆也無法再拓展時(通過-Xmx和-Xms控制),將會拋出OutOfMemoryError異常。
- Xmx Java堆最大內(nèi)存,默認值為物理內(nèi)存的1/4,當(dāng)可用的Java堆內(nèi)存大于70%時,JVM會將內(nèi)存調(diào)整至-Xms所指定的初始值
-
Xms Java堆初始內(nèi)存,默認值為物理內(nèi)存的1/64,當(dāng)可用的Java堆內(nèi)存小于40%時,JVM會將內(nèi)存調(diào)整至-Xmx所允許的最大值
方法區(qū)(重要)
- 方法區(qū)和Java堆一樣,是各個線程的共享的區(qū)域,是系統(tǒng)分配的一個內(nèi)存邏輯區(qū)域,它用于存放已被虛擬機加載的類信息(類的描述信息)、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。在HotSpot里也叫“永生代”。但兩者不能等同。如上圖,字符串池在永生代中卻在方法區(qū)外。
- 可以處于物理上不連續(xù)內(nèi)存區(qū)域,但需要邏輯上連續(xù)。
- 又叫靜態(tài)區(qū),方法區(qū)包含所有的class和static變量以及常量。
- 該區(qū)域的垃圾收集比較少見,主要針對常量池的回收和類型的卸載。
- 方法區(qū)無法滿足內(nèi)存分配需求時,會拋出OutOfMemoryError異常。
運行時常量池
- 屬于方法區(qū)的一部分。每個類私有
- 存放 Class 文件中的常量池(存放編譯期生成的各種字面量和符號引用);翻譯出來的直接引用;運行期間產(chǎn)生的新的常量(譬如 String 類的 intern() 方法)。
- 常量池?zé)o法申請到內(nèi)存, 會拋出OutOfMemoryError異常。
直接內(nèi)存

- 直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范定義中的內(nèi)存區(qū)域。但這部分區(qū)域被頻繁使用并可能引起OutOfMemoryError異常。
- NIO(New Input/Output)類中 ,可以使用 Native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在 java 堆里面的 DirectByteBuffer 對象作為這塊內(nèi)存的引用進行操作,避免了在 Java 堆和 Native 堆中來回復(fù)制數(shù)據(jù)。
- 不受 Java 堆大小的限制,但受本機總內(nèi)存的大小及處理器尋址空間的限制,會拋出 OutOfMemoryError異常。
參考:
漢森X:JVM學(xué)習(xí)01——內(nèi)存區(qū)域及內(nèi)存溢出
付小德:JVM學(xué)習(xí)心得
胖胖:https://www.zhihu.com/question/22739143
Emanue:Java中幾種常量池的區(qū)分
