Java8記憶體結構圖

虛拟機記憶體與本地記憶體的差別
Java虛拟機在執行的時候會把管理的記憶體配置設定成不同的區域,這些區域被稱為虛拟機記憶體,同時,對于虛拟機沒有直接管理的實體記憶體,也有一定的利用,這些被利用卻不在虛拟機記憶體資料區的記憶體,我們稱它為本地記憶體,這兩種記憶體有一定的差別:
JVM記憶體
- 受虛拟機記憶體大小的參數控制,當大小超過參數設定的大小時就會報OOM
本地記憶體
- 本地記憶體不受虛拟機記憶體參數的限制,隻受實體記憶體容量的限制
- 雖然不受參數的限制,但是如果記憶體的占用超出實體記憶體的大小,同樣也會報OOM
java運作時資料區域
java虛拟機在執行過程中會将所管理的記憶體劃分為不同的區域,有的随着線程産生和消失,有的随着java程序産生和消失。
程式計數器(Program Counter Register)
程式計數器就是目前線程所執行的位元組碼的行号訓示器,通過改變計數器的值,來選取下一行指令,通過他來實作跳轉、循環、恢複線程等功能。
- 在任何時刻,一個處理器核心隻能運作一個線程,多線程是通過線程輪流切換,配置設定時間來完成的,這就需要有一個标志來記住每個線程執行到了哪裡,這裡便需要到了程式計數器。
- 是以,程式計數器是線程私有的,每個線程都有自己的程式計數器。
虛拟機棧(JVM Stacks)
虛拟機棧是線程私有的,随線程生滅。虛拟機棧描述的是線程中的方法的記憶體模型:
每個方法被執行的時候,都會在虛拟機棧中同步建立一個棧幀(stack frame)。
每個棧幀的包含如下的内容
- 局部變量表
-
- 局部變量表中存儲着方法裡的java基本資料類型(byte/boolean/char/int/long/double/float/short)以及對象的引用(注:這裡的基本資料類型指的是方法内的局部變量)
- 操作數棧
- 動态連接配接
- 方法傳回位址
方法被執行時入棧,執行完後出棧
虛拟機棧可能會抛出兩種異常:
- 如果線程請求的棧深度大于虛拟機所規定的棧深度,則會抛出StackOverFlowError即棧溢出
- 如果虛拟機的棧容量可以動态擴充,那麼當虛拟機棧申請不到記憶體時會抛出OutOfMemoryError即OOM記憶體溢出
本地方法棧(Native Method Stacks)
本地方法棧與虛拟機棧的作用是相似的,都會抛出OutOfMemoryError和StackOverFlowError,都是線程私有的,主要的差別在于:
- 虛拟機棧執行的是java方法
- 本地方法棧執行的是native方法
Java堆(Java Heap)
java堆是JVM記憶體中最大的一塊,由所有線程共享,是由垃圾收集器管理的記憶體區域,主要存放對象執行個體,當然由于java虛拟機的發展,堆中也多了許多東西,現在主要有:
- 對象執行個體
-
- 類初始化生成的對象
- 基本資料類型的數組也是對象執行個體
- 字元串常量池
-
- 字元串常量池原本存放于方法區,jdk7開始放置于堆中。
- 字元串常量池存儲的是string對象的直接引用,而不是直接存放的對象,是一張string table
- 靜态變量
-
- 靜态變量是有static修飾的變量,jdk7時從方法區遷移至堆中
- 線程配置設定緩沖區(Thread Local Allocation Buffer)
-
- 線程私有,但是不影響java堆的共性
- 增加線程配置設定緩沖區是為了提升對象配置設定時的效率
java堆既可以是固定大小的,也可以是可擴充的(通過參數-Xmx和-Xms設定),如果堆無法擴充或者無法配置設定記憶體時也會報OOM。
方法區(Method Area)
方法區是所有線程共享的記憶體,在java8以前是放在JVM記憶體中的,由永久代實作,受JVM記憶體大小參數的限制,在java8中移除了永久代的内容,方法區由元空間(Meta Space)實作,并直接放到了本地記憶體中,不受JVM參數的限制(當然,如果實體記憶體被占滿了,方法區也會報OOM),并且将原來放在方法區的字元串常量池和靜态變量都轉移到了Java堆中,方法區與其他區域不同的地方在于,方法區在編譯期間和類加載完成後的内容有少許不同,不過總的來說分為這兩部分:
類元資訊(Klass)
- 類元資訊在類編譯期間放入方法區,裡面放置了類的基本資訊,包括類的版本、字段、方法、接口以及常量池表(Constant Pool Table)
- 常量池表(Constant Pool Table)存儲了類在編譯期間生成的字面量、符号引用
- 運作時常量池主要存放在類加載後被解析的字面量與符号引用,但不止這些
- 運作時常量池具備動态性,可以添加資料,比較多的使用就是String類的intern()方法