1. 程式人生 > >1.Java執行時記憶體區域

1.Java執行時記憶體區域

Java執行時記憶體區域

JVM虛擬機器執行時資料區域包括程式計數器,Java虛擬機器棧,本地方法棧,Java堆,方法區。此外,java程式執行時也會經常用到本地記憶體。關於它們的記憶體分佈圖如下所示: 在這裡插入圖片描述 在這裡插入圖片描述

下面具體介紹下上面提到的幾塊記憶體區域:

程式計數器

一塊較小的記憶體空間,它是當前執行緒所執行的位元組碼的行號指示器,位元組碼直譯器工作時通過改變該計數器的值來選擇下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都要依賴它來實現。

如果執行緒在執行一個Java方法時,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果在執行的是Native方法時,這個計數器的值為空。

Java虛擬機器棧

虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀,用於儲存區域性變量表、運算元棧、動態連結、方法返回地址和一些額外的附加資訊。

在編譯程式程式碼時,棧幀中需要多大的區域性變量表、多深的運算元棧都已經完全確定了,並且寫入了方法表的Code屬性之中。

本地方法棧

本地方法棧與虛擬機器棧所發揮的作用非常相似,只是虛擬機器棧為虛擬機器執行Java方法服務,而本地方法棧則為使用到的Native方法服務。

Java堆

Java堆是Java虛擬機器所管理的記憶體中最大的一塊,它是所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。所有的物件例項以及陣列都要在堆上分配記憶體。Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱為“GC堆”。 從記憶體回收角度看可以細分為:新生代和老年代;從記憶體私有與共享的使用角度看java堆可能劃分出多個執行緒私有的分配緩衝區。

方法區(元空間)

方法區也是各個執行緒共享的記憶體區域,它用於儲存已經被虛擬機器載入的類資訊、常量、靜態變數、即時編譯後的程式碼等資料。

Hotspot在jdk1.6之前使用永久帶來實現方法區。JDK1.7中,儲存在永久代的部分資料被轉移到了Java Heap或者是 Native Memory(Native Heap)。但永久代仍存在於JDK1.7中,並沒完全移除,譬如符號引用(Symbols)轉移到了Native Memory;字面量(interned strings)轉移到了java heap;類的靜態變數(class statics)轉移到了java heap。 jdk8使用了元空間來實現方法區。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用Native Memory。因此,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過以下引數來指定元空間的大小:

-XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行型別解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,適當提高該值。 -XX:MaxMetaspaceSize,最大空間,預設是沒有限制的。

執行時常量池

該區域用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類載入後存放到方法區的執行時常量池中。執行時常量池相對於Class檔案常量池的另一個重要特徵是具備動態性,Java語言並不要求常量一定只能在編譯期產生,也就是並非預置入Class檔案中的常量池的內容才能進入方法區的執行時常量池,執行期間也可能將新的常量放入池中,這種特性被開發人員利用比較多的是String類的intern()方法。 在JDK1.8中,符號引用(Symbols)轉移到了Native Memory;字面量(interned strings)轉移到了java heap;類的靜態變數(class statics)轉移到了java heap。

本地記憶體Native Memory(Native Heap)

本地記憶體並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,它直接從作業系統中分配,是java程序地址空間,因此不受Java堆大小的限制,但是會受到本機總記憶體的大小及處理器定址空間的限制,因此它也可能導致OutOfMemoryError異常出現。

在JDK1.4中新引入了NIO機制,它是一種基於通道與緩衝區的新I/O方式,可以使用Native函式直接分配對外內,然後通過一個儲存在Java堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作。這樣能在一些場景中提高效能,避免了在Java堆和Native堆中來回複製資料。這種情況下的本地記憶體稱之為Directory Memory。另外,DirectByteBuffer的引用物件是直接分配在堆得Old區的,因此其回收時機是在FullGC時。因此,需要避免頻繁的分配DirectByteBuffer,這樣很容易導致Native Memory溢位。