Java虛拟機在執行java程式的過程中會把它所管理的記憶體劃分為若幹個不同的資料區域.根據 <<java虛拟機規範>> 中的規定,将記憶體區域劃分為,
程式計數器(Program Counter Register)
虛拟機棧(VM Stack)
本地方法棧(Native Method Stack)
和
方法區(Method Area)
五大區域.
堆(Heap)

運作時記憶體區域
程式計數器(Program Counter Register)
程式計數器是一塊很小的記憶體區域,可以當成目前線程所執行的位元組碼的行号訓示器.java解釋器通過改變計數器值來選取下一條指令.分治,循環,跳轉,異常處理,線程恢複等需要依賴計數器完成
特點:
- 每一個線程都有一個獨立的程式計數器,互不影響.(線程私有)
- 線程執行Java方法,計數器記錄的是正在執行的虛拟機位元組碼指令的位址.
- 線程執行Native方法,計數器則為空.
- 唯一一個沒有定義
的區域.OutOfMemoryError
虛拟機棧(VM Stack)
虛拟機棧它的棧元素是一種叫做
棧幀(Stack Frame)
的結構.每一個棧幀都包括了
局部變量表(Local Variable Table)
操作數棧(Operand Stack)
動态連結(Dynamic Linking)
方法傳回位址(Return Address)
和一些額外資訊.
棧幀是用于支援虛拟機進行方法調用和方法執行的資料結構,每一個方法從調用開始至執行完成的過程,都對應着一個棧幀在虛拟機棧裡面從入棧到出棧的過程.
棧幀結構
- Java虛拟機棧是線程私有的,生命周期與線程一緻
- 局部變量表所需的記憶體空間在編譯期間确定,并完成配置設定.
- 在方法運作期間不會改變局部變量表的大小.
- 如果請求的棧深度大于虛拟機允許的深度,抛出
.StackOverflowError
- 虛拟機棧擴充時無法申請足夠的記憶體,抛出
OutOfMemoryError
本地方法棧(Native Method Stack)
功能與虛拟機棧類似,java線程在調用本地方法時,該區用來存儲本地方法的局部變量表,本地方法的操作數棧等等資訊.差別在于虛拟機棧執行的是java方法,
本地方法棧執行的是native方法(c/c++方法).
java是進階程式設計語言,當對一些底層的如作業系統或某些硬體交換資訊時,我們使用java來程式設計實作起來不容易,再者使用java來程式設計效率也很低下.這時候就可以通過
JNI
方式來調用 native方法來實作.
本地方法棧與java棧
如果,展示了java與native方法互動的過程,java方法中調用了C語言方法,産生在本地方法棧中産生一個本地棧幀,這個C語言方法調用了另一個C語言方法,并且把結果回調回java方法中.
一個線程可能在整個生命周期中都執行Java方法,操作他的Java棧;或者他可能毫無障礙地在Java棧和本地方法棧之間跳轉。
特點 :
- 線程私有,生命周期與線程一緻
- 調用的是 c/c++方法(一般用于底層互動,或者性能優化)
- 可抛出
StackOverflowError
OutOfMemoryError
java堆(Heap)
java虛拟機中最大的記憶體區域,幾乎所有類執行個體和數組的記憶體均從此處配置設定。
- 線程共享
- 在 Java 虛拟機啟動時建立的
- GC管理的主要區域
- 可位于實體記憶體不連續的空間.
- 可以是固定大小的,也可以是可擴充的.
- 在沒有記憶體空間并且無法擴充時,抛出
OutOfMemoryError
hotspot中的實作
在hotspot虛拟機中,從記憶體回收的角度來看是采用 分代收集政策.将堆劃分為兩個不同的區域:
新生代(Young Gen)
老年代(Old Gen)
堆的空間大小 = 新生代 + 老年代; 預設情況下,新生代和老年代的比例是 1:2;
新生代又被劃分為
Eden
From Survivor
To Survivor
三個區域;大小比例為 8:1:1
由于新生代采用
複制算法
來管理空間,是以,無論什麼時候,總是有一塊 Survivor 區域是空閑着的。
新生代實際可用的記憶體空間為90%的新生代空間。
方法區(Mthod Area)
方法區中,存儲着已加載的類資訊,常量,靜态變量,即時編譯後的代碼等資料.
其中類相關的資訊,如類名,通路修飾符,常量池,字段描述,方法描述等.
方法區邏輯上屬于堆的一部分,但是為了與堆進行區分,通常又叫“非堆”。
方法區的資料是線程共享的.
為何叫方法區? 方法區中除了包括“已加載的類的基本資訊、常量、靜态變量等”外,還包括編譯器編譯後的代碼,而且這應該是方法區中主要的一部分,這可能是為何把這部分記憶體成為方法區的原因.
注釋 : 類的對象和執行個體對象存放在 java堆中, 類的中繼資料存放在 方法區中.
不同jdk(hotspot)版本,方法區資料的變化
JDK 1.6以及之前,方法區的實作為
永久代(Permanent Gen)
的方式,目的是為了垃圾收集器能像管理java堆一樣管理這部分記憶體.垃圾回收目标是針對常量池的回收和對類型的解除安裝.
JDK 1.7中,存儲在永久代的部分資料就已經轉移到Java Heap或者Native Heap。但永久代仍存在于JDK 1.7中,并沒有完全移除,如符号引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了Java heap;類的靜态變量(class statics)存放于定義類型的class對象中,存放在Java heap中.
JDK 1.8中, 完全移除了永久代,取而代之的實作方式成為
元空間(Metaspace)
,将類中繼資料放到本地記憶體中,将字元常量池和靜态變量放到Java堆裡。虛拟機會為類的中繼資料明确配置設定和釋放本地記憶體。
元空間的本質和永久代類似,都是對JVM規範中方法區的實作。不過元空間與永久代之間的最大差別在于:元空間并不在虛拟機中,而是使用本地記憶體。
Native memory:本地記憶體,也稱為C-Heap,是供JVM自身程序使用的。當Java Heap空間不足時會觸發GC,但Native memory空間不夠卻不會觸發GC。即GC不管理元空間(Metaspace)的記憶體.
為什麼移除永久代?
- 字元串存在永久代中,容易出現性能問題和記憶體溢出。
- 永久代大小不容易确定,PermSize指定太小容易造成永久代OOM
- 永久代會為 GC 帶來不必要的複雜度,并且回收效率偏低。
- Oracle 可能會将HotSpot 與 JRockit 合二為一。