JAVA 內存
- 一、程序計數器
- 二、虛擬機棧
- 三、本地方法棧
- 四、堆
- 五、非JAVA內存(堆外內存)
- 1.元空間(Metaspace)
- 2.直接內存
鏈接: jvm學習筆記(二) ----- 垃圾回收
鏈接: jvm學習筆記(三) ----- 垃圾回收器
一、程序計數器
- 虛擬機需要通過『程序計數器』記錄指令執行到哪了。
- 線程要輪流使用 CPU 時間片,因此需要『程序計數器』來記住正在執行的字節碼的地址。例如
線程 A
的計數器記錄當前執行到了第三行字節碼,這時候時間片用完了,CPU 切換到其它線程運行,當 CPU 再次切換到線程 A
時,它就會從計數器得知上次執行的代碼位置,繼續向下運行。
二、虛擬機棧
- 作用: 一個線程使用的內存大小。線程內調用一次方法,就會產生一個棧幀,棧幀內包含方法內局部變量,方法參數,返回地址等。多個棧幀合稱為『棧』,而正在執行的方法稱為『活動棧幀』,一個線程內同一時刻只能有一個『活動棧幀』
- 配置:
-XssThe default value depends on the platform:
* Linux/x64 (64-bit): 1024 KB
* macOS (64-bit): 1024 KB
* Oracle Solaris/x64 (64-bit): 1024 KB
* Windows: The default value depends on virtual memory
*
- 特點:
- 方法執行完畢,棧幀內存即被釋放
- 因為線程私有,不存在共享,因此線程安全
- 值越大,會讓線程數更少
- 棧內存溢出情況
- 棧太小,方法調用過深(棧幀太多)
- 棧太小,方法內局部變量太多(棧幀太大)
- 測試代碼如下:
public class Demo1 {private static int count = 0;public static void main(String[] args) {method1();}private static void method1() {count ++ ;System.out.println(count);method1();}}
三、本地方法棧
- 每個線程啟動時,還會分配『本地方法棧』內存,來給哪些其它語言實現的方法(稱為本地方法)使用。
+
四、堆
- Java堆通常是Java虛擬機所管理的內存中最大的一塊。Java堆是被鎖有線程共享的一塊內存區域,在虛擬機啟動時創建。這塊區域唯一的目的就是存放對象實例,幾乎所有對象實例及數組都在該區域分配內存,從 JDK1.7 開始,StringTable等也會使用堆內存。
- Java堆時垃圾收集器管理的主要區域(GC堆),從內存回收的角度(收集器一般采用分代收集算法),堆被劃分為新生代和舊生代,新生代又被進一步劃分為Eden(伊甸園) 和 Survivor(幸存區) 區,最后Survivor由FromSpace和ToSpace組成,結構圖如下所示:
- 堆空間內存分配(默認情況下)
-
老年代 : 三分之二的堆空間
-
年輕代 : 三分之一的堆空間
-
eden區: 8/10 的年輕代空間
-
survivor From : 1/10 的年輕代空間
-
survivor To : 1/10 的年輕代空間
五、非JAVA內存(堆外內存)
1.元空間(Metaspace)
-
作用:用來存儲類對象,類加載器,靜態變量,StringTable,SymbolTable,即時編譯器生成的代碼等。
-
歷史:
- 『方法區』是 Java VM 規范中定義的概念,具體實現根據各個虛擬機廠商的不同而不同。對于 Oracle 的 HotSpot 虛擬機來說,最初作為『方法區』的實現稱之為『永久代』,從 Java 8 開始,『永久代』被替換為『元空間』。
- 『永久代』,垃圾回收仍然會考慮『永久代』,但回收效率不高,StringTable 最初也使用的是『永久代』內存,容易造成 OOM 問題。
- 『元空間』,使用了操作系統內存,默認沒有上限。并且 StringTable 的空間被移至堆內存,『元空間』中僅存儲類加載器、類對象等信息,垃圾回收不用考慮『元空間』,元空間自己管理內存釋放。
2.直接內存
- 定義:在 NIO 進行 IO 操作時,用到的數據緩沖內存 DirectBuffer
- 特點:典型實現由 DirectByteBuffer,它使用了堆外內存,可以用 allocateDirect 方法創建
- 好處:
- 沒有使用堆內存,減少 GC 壓力
- I/O 讀寫操作直接操作堆外內存,省去了系統空間和用戶空間的數據拷貝
- 堆外內存回收通過虛引用實現