JVM運行時數據區 — 堆
堆的核心概述
首先 我們先了解堆空間位於運行時數據區的哪一個位置
-
在這裏我們強調一個概念 一個進程對應一個JVM實例 一個JVM實例對應一個運行時數據區 運行時數據區獨立的只有一個方法區和一個堆 也就是說一個進程裏的多個線程要共享一個方法區和堆空間 而每個線程各自擁有一份程序計數器 本地方法棧 和 虛擬機棧
-
Java虛擬機規範中對Java堆的描述是 所有的對象實例和數組都應該在運行時分配在堆上
-
數組和對象可能永遠不會存儲在棧上 因爲棧幀當中保存引用 這個引用指向對象或者數組在堆中的位置
-
在方法結束後 堆中的對象不會被馬上的移除 僅僅在垃圾回收的時候纔會被移除
-
堆的內存細分
設置堆內存大小與OOM
-
-Xms用來設置堆空間的初始內存大小 -Xmx用來設置堆空間的最大內存大小
-
默認情況下 堆空間的大小
初始內存大小 物理電腦內存大小/64
最大內存大小 物理電腦內存/4
-
查看設置的參數
方式一 jps / jstat -gc 進程id
方式二 -XX:printGCDetails
-
OOM
此時發生了OOM異常 java.lang.OutOfMemoryError: Java Heap space
年輕代與老年代
- 默認情況 新生代與老年代的比例是1:2
- 默認情況 伊甸園與兩個倖存者的比例是8:1:1
- 當我們加上 -XX: SurvivorRatio: 8 我們看到的結果就是8:1:1
圖解對象分配過程
1.首先 但我們最先開始new 對象的時候 我們將對象放到伊甸園區 當我們伊甸園區放滿的時候 就會觸發一次YGC 這時 我們先把垃圾先回收掉 也就是上圖的紅色部分是垃圾就會被回收了同時 我們將還存活的對象移動到倖存者0區 並將他們的年齡+1
2 通過上一次GC 我們伊甸園區已經空了 這是我們又在伊甸園區不斷的new對象 直到又一次把伊甸園區放滿了
這個時候我們就觸發了第二次GC 這個時候我們進行2個操作
第一 把Eden中存活的對象移動到s1區
第二 對s0區的對象做一次判斷 看他們是不是垃圾現在 如果不是 就把s0區當中存活的對象移動到s1區
每次執行完gc之後 這兩個倖存者區當中誰空誰就是to區 這個to區表明了下一次YGC的時候Eden區中存活的對象 要往哪裏放
3 我們來看一種較爲特殊的情況 當我們執行一次YGC之後 我們還是按照剛剛的兩步(這時s0區是空的 作爲to區) 先是將Eden中的對象放到空的s0區當中 然後將s1區當中年齡是1的對象也放到s0區當中 然後把s1區當中年齡爲15的對象晉升到了老年區
特別的 這裏有一個注意點
什麼時候會觸發YGC
觸發YGC的條件是 當伊甸園區滿的時候會觸發YGC
survivor區滿了的時候不會
觸發YGC 這個一定注意
當觸發YGC/Minor GC的時候 會將Eden中和survivor區中的對象一起回收
Minor GC — Major GC — Full GC
注意 Major GC只是老年代的垃圾收集 Full GC是整個Java 堆和方法區的收集
- Minor GC的觸發機制
- 當年輕代空間不足會觸發Minor GC 這裏的年輕代滿指的是Eden滿了 Survivor滿不會觸發GC
- Java對象大多朝生夕死 所以Minor GC非常頻繁
- Minor GC會引發STW 暫停用戶線程 等垃圾回收結束 用戶線程才恢復運行
內存分配策略
-
優先分配到Eden
-
大對象直接分配到老年代
-
長期存活的對象分配到老年代