JVM 內存區域總結:方法區+堆內存+本地方法棧+元空間——JVM系列(一)

前言

在JVM的管控下,Java程序員不再需要管理內存的分配與釋放,這和在C和C++的世界是完全不一樣的。所以,在JVM的幫助下,Java程序員很少會關注內存泄露和內存溢出的問題。但是,一旦JVM發生這些情況的時候,如果你不清楚JVM內存的內存管理機制是很難定位與解決問題的。

一、JVM 內存區域

Java虛擬機在運行時,會把內存空間分爲若干個區域,根據《Java虛擬機規範(Java SE 7 版)》的規定,Java虛擬機所管理的內存區域分爲如下部分:方法區、堆內存、虛擬機棧、本地方法棧、程序計數器。

image

1、方法區

方法區主要用於存儲虛擬機加載的類信息、常量、靜態變量,以及編譯器編譯後的代碼等數據。在jdk1.7及其之前,方法區是堆的一個“邏輯部分”(一片連續的堆空間),但爲了與堆做區分,方法區還有個名字叫“非堆”,也有人用“永久代”(HotSpot對方法區的實現方法)來表示方法區。

從jdk1.7已經開始準備“去永久代”的規劃,jdk1.7的HotSpot中,已經把原本放在方法區中的靜態變量、字符串常量池等移到堆內存中,(常量池除字符串常量池還有class常量池等),這裏只是把字符串常量池移到堆內存中;在jdk1.8中,方法區已經不存在,原方法區中存儲的類信息、編譯後的代碼數據等已經移動到了元空間(MetaSpace)中,元空間並沒有處於堆內存上,而是直接佔用的本地內存(NativeMemory)。根據網上的資料結合自己的理解對jdk1.3~1.6、jdk1.7、jdk1.8中方法區的變遷畫了張圖如下(如有不合理的地方希望讀者指出):

image

去永久代的原因有:

(1)字符串存在永久代中,容易出現性能問題和內存溢出。

(2)類及方法的信息等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢出,太大則容易導致老年代溢出。

(3)永久代會爲 GC 帶來不必要的複雜度,並且回收效率偏低。

2、堆內存

堆內存主要用於存放對象和數組,它是JVM管理的內存中最大的一塊區域,堆內存和方法區都被所有線程共享,在虛擬機啓動時創建。在垃圾收集的層面上來看,由於現在收集器基本上都採用分代收集算法,因此堆還可以分爲新生代(YoungGeneration)和老年代(OldGeneration),新生代還可以分爲 Eden、From Survivor、To Survivor。

3、程序計數器

程序計數器是一塊非常小的內存空間,可以看做是當前線程執行字節碼的行號指示器,每個線程都有一個獨立的程序計數器,因此程序計數器是線程私有的一塊空間,此外,程序計數器是Java虛擬機規定的唯一不會發生內存溢出的區域。

4、虛擬機棧

虛擬機棧也是每個線程私有的一塊內存空間,它描述的是方法的內存模型,直接看下圖所示:

image

虛擬機會爲每個線程分配一個虛擬機棧,每個虛擬機棧中都有若干個棧幀,每個棧幀中存儲了局部變量表、操作數棧、動態鏈接、返回地址等。一個棧幀就對應 Java 代碼中的一個方法,當線程執行到一個方法時,就代表這個方法對應的棧幀已經進入虛擬機棧並且處於棧頂的位置,每一個 Java 方法從被調用到執行結束,就對應了一個棧幀從入棧到出棧的過程。

5、本地方法棧

本地方法棧與虛擬機棧的區別是,虛擬機棧執行的是 Java 方法,本地方法棧執行的是本地方法(Native Method),其他基本上一致,在 HotSpot 中直接把本地方法棧和虛擬機棧合二爲一,這裏暫時不做過多敘述。

6、元空間

上面說到,jdk1.8 中,已經不存在永久代(方法區),替代它的一塊空間叫做 “ 元空間 ”,和永久代類似,都是 JVM 規範對方法區的實現,但是元空間並不在虛擬機中,而是使用本地內存,元空間的大小僅受本地內存限制,但可以通過 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 來指定元空間的大小。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章