運行時數據區
JVM 由三部分組成:類加載系統、運行時數據區、執行引擎
下邊講一下運行時數據區中的構成
根據線程的使用情況分為兩類:
- 線程獨享(此區域不需要垃圾回收)
- 虛擬機棧、本地方法棧、程序計數器
- 線程共享(數據存儲區域,此區域需要垃圾回收)
- 存儲類的靜態數據和對象數據
- 堆和方法區
堆
Java 堆在 JVM 啟動時創建內存區域去實現對象、數組與運行時常量的內存分配,它是虛擬機管理最大的,也是垃圾回收的主要內存區域
在 JDK1.8 中,堆由兩部分組成:新生代和老年代
而在 JDK1.9 中,取消了新生代和老年代的物理劃分,將堆劃分為若干個區域 Region,如下圖:
可以通過代碼查看堆空間的大小:
public class HeapSpaceInitial {public static void main(String[] args) {/**使用Runtime.getRuntime()獲取當前 (運行時數據區) , 是單例的。*/// 返回Java虛擬機中的堆內存總量long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;// 返回Java虛擬機試圖使用的最大堆內存量long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;System.out.println("-Xms: " + initialMemory + "M");System.out.println("-Xmx: " + maxMemory + "M");System.out.println("系統初始內存大小為: " + initialMemory * 64.0 / 1024 + "G");System.out.println("系統最大內存大小為: " + maxMemory * 4.0 / 1024 + "G");/**輸出:-Xms: 243M-Xmx: 3609M系統初始內存大小為: 15.1875G系統最大內存大小為: 14.09765625G*/}
}
通過命令行查看堆中的參數:
jps # 查看運行的進程
jstat -gc 進程id # 查看該進程的堆中參數
通過 VM options 查看垃圾回收時的信息:
-XX:+PrintGCDetails
Java 中新創建的對象如何分配空間呢?
- new 的對象先放 Eden 區(如果是大對象,直接放入老年代)
- 當 Eden 區滿了之后,程序還需要創建對象,則垃圾回收器會對 Eden 區進行垃圾回收
- 在垃圾回收的時候,會將 Eden 區的幸存對象轉移到 Survivor From 區
- 如果再次觸發垃圾回收,此時將 Eden 區的幸存對象轉移到 Survivor To 區中,并且將 Survivor From 區中的幸存對象也轉移到 Survivor To 區
- 如果再次出發垃圾回收,此時將 Eden 區和 Survivor To 區中的幸存對象轉移到 Survivor From 區中
- 當對象的生存年齡達到 15 時,會被放入老年代
在幸存對象每次轉移的時候,對會將對象的生存年齡 + 1,達到 15 時會放入老年代中
Java 對象只會分配在堆中嗎?
不是的,如果經過 逃逸分析
后發現,一個對象并沒有逃逸出方法的話,就可能被優化為在棧上分配
,這是常見的堆外存儲技術。
逃逸分析就是分析對象動態作用域:
- 對象在方法中被定義后,對象只在方法內部使用,則認為沒有發生逃逸
- 對象在方法中被定義后,對象被外部方法所引用,則認為發生逃逸