JVM 09.1 執行時資料區 堆 核心概述
版權宣告:源出處:尚矽谷JVM
部落格來源於大佬整理
一個程序對應一個jvm例項,一個執行時資料區,又包含多個執行緒,這些執行緒共享了方法區和堆,每個執行緒包含了程式計數器、本地方法棧和虛擬機器棧。
核心概述
1.一個jvm例項只存在一個堆記憶體,堆也是java記憶體管理的核心區域
2.Java堆區在JVM啟動的時候即被建立,其空間大小也就確定了。是JVM管理的最大一塊記憶體空間(堆記憶體的大小是可以調節的)
3.《Java虛擬機器規範》規定,堆可以處於物理上不連續的記憶體空間中,但在邏輯上它應該被視為連續的。
4.所有的執行緒共享java堆,在這裡還可以劃分執行緒私有的緩衝區(TLAB:Thread Local Allocation Buffer).(面試問題:堆空間一定是所有執行緒共享的麼?不是,TLAB執行緒在堆中獨有的)
5.《Java虛擬機器規範》中對java堆的描述是:所有的物件例項以及陣列都應當在執行時分配在堆上。從實際使用的角度看,“幾乎”所有的物件的例項都在這裡分配記憶體 (‘幾乎’是因為可能儲存在棧上,另見逃逸分析)
6。陣列或物件永遠不會儲存在棧上,因為棧幀中儲存引用,這個引用指向物件或者陣列在堆中的位置。
7.在方法結束後,堆中的物件不會馬上被移除,僅僅在垃圾收集的時候才會被移除
8.堆,是GC(Garbage Collection,垃圾收集器)執行垃圾回收的重點區域
配置堆記憶體及檢視jvm程序
編寫HeapDemo/HeapDemo1程式碼:
public class HeapDemo {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
首先對虛擬機器進行配置,如圖 Run-Edit configurations:
可以看到HeapDemo配置-Xms10m, 分配的10m被分配給了新生代3m和老年代7m:
分析SimpleHeap的jvm情況
public class SimpleHeap {
private int id;//屬性、成員變數
public SimpleHeap(int id) {
this.id = id;
}
public void show() {
System.out.println("My ID is " + id);
}
public static void main(String[] args) {
SimpleHeap sl = new SimpleHeap(1);
SimpleHeap s2 = new SimpleHeap(2);
int[] arr = new int[10];
Object[] arr1 = new Object[10];
}
}
堆的細分記憶體結構
JDK 7以前: 邏輯上分為新生區+養老區+永久區(即Xms/Xmx分配的記憶體物理上沒有涉及永久區)
- Young Generation Space:又被分為Eden區和Survior區
- Tenure generation Space: ==Old/Tenure==
- Permanent Space: ==Perm==
- Young Generation Space:又被分為Eden區和Survior區
- Tenure generation Space: ==Old/Tenure==
- Meta Space: ==Meta==
設定堆記憶體大小與OOM
1.Java堆區用於儲存java物件例項,堆的大小在jvm啟動時就已經設定好了,可以通過 "-Xmx"和 "-Xms"來進行設定
- -Xms 用來設定堆空間(年輕代+老年代)的初始記憶體大小,等價於 -XX:InitialHeapSize
- -X 是jvm的執行引數
- ms 是memory start
- -Xmx 用於設定堆的最大記憶體,等價於 -XX:MaxHeapSize
2.一旦堆區中的記憶體大小超過 -Xmx所指定的最大記憶體時,將會丟擲OOM異常。
- 預設情況下,初始記憶體大小:實體記憶體大小/64;最大記憶體大小:實體記憶體大小/4。
- 手動設定:-Xms600m -Xmx600m
3.通常會將-Xms和-Xmx兩個引數配置相同的值,其目的就是為了能夠在java垃圾回收機制清理完堆區後不需要重新分隔計算堆區的大小,從而提高效能。
- 比如說:預設空餘堆記憶體小於40%時,JVM就會增大堆直到-Xmx的最大限制;空餘堆記憶體大於70%時,JVM會減少堆直到 -Xms的最小限制。
因此伺服器一般設定-Xms、-Xmx相等以避免在每次GC 後調整堆的大小
4.檢視設定的堆記憶體引數:
- 方式一: ==終端輸入jps== , 然後 ==jstat -gc 程序id==
- 方式二:(控制檯列印)Edit Configurations->VM Options 新增 ==-XX:+PrintGCDetails==
檢視堆記憶體大小測試程式碼
public class HeapSpaceInitial {
public static void main(String[] args) {
//返回Java虛擬機器中的堆記憶體總量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
//返回Java虛擬機器試圖使用的最大堆記憶體量
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms : " + initialMemory + "M");//-Xms : 245M
System.out.println("-Xmx : " + maxMemory + "M");//-Xmx : 3641M
System.out.println("系統記憶體大小為:" + initialMemory * 64.0 / 1024 + "G");//系統記憶體大小為:15.3125G
System.out.println("系統記憶體大小為:" + maxMemory * 4.0 / 1024 + "G");//系統記憶體大小為:14.22265625G
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
堆大小分析
設定堆大小為600m,打印出的結果為575m,這是因為倖存者區S0和S1各佔據了25m,但是他們始終有一個是空的,存放物件的是伊甸園區和一個倖存者區。
OOM示例
java.lang.OutOfMemoryError: Java heap space。程式碼示例:
/**
* -Xms600m -Xmx600m
*/
public class OOMTest {
public static void main(String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while(true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(new Picture(new Random().nextInt(1024 * 1024)));
}
}
}
class Picture{
private byte[] pixels;
public Picture(int length) {
this.pixels = new byte[length];
}
}