大綱
- Java虛擬機運行時資料區
- 會發生OOM和SOF例外的記憶體區域
- 堆
- 虛擬機堆疊
- 本地方法堆疊
- 方法區
- 本機直接記憶體
Java虛擬機運行時資料區
OOM和SOF都是記憶體溢位例外,與java記憶體區域的關系密不可分,所以要先了解java各個記憶體區域

會發生OOM和SOF例外的記憶體區域
首先明確的是,程式計數器是在《java虛擬機規范中》唯一一個沒有規定任何OutOfMemoryError情況的記憶體區域,像其它方法區、虛擬機堆疊、本地方法堆疊、堆都有可能會拋出OOM和SOF例外,下面會對各個記憶體區域進行細說,
堆
? 堆是java虛擬機所管理的記憶體中的最大一個區域,所有的物件實體以及陣列都應該在堆上,一般情況下,堆都是可以擴展的,通過(-Xmx 和 -Xms 來設定),如果堆在分配實體記憶體時超過了最大的可擴展記憶體,就會拋出OutOfMemoryError例外,下面來模擬以下堆的OOM例外
//設定引數:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
//HeapDumpOnOutOfMemoryError可以在發生堆記憶體溢位的時候,拋出一個快照檔案,方便事后處理
public class OutOfMemoryErrorTest {
public static void main(String[] args) {
ArrayList<OutOfMemoryErrorTest> list = new ArrayList<>();
while (true){
list.add(new OutOfMemoryErrorTest());
}
}
}
得到快照檔案

下面是快照檔案.hprof里的內容

首先我們需要確定到底是記憶體泄漏還是記憶體溢位,我們發現導致OOM的物件就是OutOfMemoryErrorTest,而這個物件是有用的,所以要解決OOM就要檢查(-Xmx 和 -Xms)引數的設定,與機器記憶體進行對比,看看還能不能向上調整空間,如果導致OOM的物件是無用的,也就是說它本來該被垃圾回收,所以我們需要檢查GC Roots,看看該物件是怎么與GC Roots發生關聯才導致無法被回收的,
虛擬機堆疊
除了JDK1版本的虛擬機,一般情況下虛擬機是不允許堆疊的記憶體動態擴展的,所以說只要執行緒請求的堆疊的深度大于虛擬機所允許的最大深度,將會拋出StackOverflowError,我們可以通過 -Xss 設定java虛擬機最大允許堆疊的深度,下面來模擬以下堆疊溢位例外
public class StackOverTest {
private int i=1;
public void stackOver(){
i++;
//執行緒呼叫一個方法就會同步創建一個堆疊幀壓入到運算元堆疊里面
stackOver();
}
public static void main(String[] args) {
StackOverTest stackOverTest = new StackOverTest();
stackOverTest.stackOver();
}
}

本地方法堆疊
本地方法堆疊和虛擬機堆疊一樣,也會在堆疊深度溢位或者堆疊擴展失敗的時候分別拋出 StackOverflowError 和 OutOfMemoryError 例外,
方法區
在jdk6的時候,有方法區屬于永久代,可以通過 -XX:PermSize 和 -XX:MaxPermSize 來限制永久代的大小,如果超過了最大值那么就會拋出OOM例外,而在jdk7中把永久代中的字串常量池、靜態變數等移出,放到堆中,到了jdk8完全取消了永久代,而使用元空間來代替它,把jdk7中剩余的內容例如類資訊等全部移到元空間中,換句話說,jdk8使用了元空間來實作方法區,而元空間記憶體不在虛擬機的記憶體中,而是在機器本地記憶體,但我們仍可以通過以下幾個引數來控制元空間大小和垃圾回收,
- -XX:MaxMetaspaceSize:設定元空間的最大值
- -XX:MetaspaceSize:指定元空間的初始化大小
- -XX:MinMetaspaceFreeRatio:設定垃圾回收后控制元空間最小的剩余量
- -XX:MetaspaceFreeRatio:設定垃圾回收后元空間剩余容量百分比
本機直接記憶體
直接記憶體可以通過指定 -XX:MaxDirectMemorySize來指定,如果超過了該值會拋出OOM例外,我們通過unsafe類來直接分配記憶體,來模擬出OOM例外
//引數設定:-Xmx20M -XX:MaxDirectMemorySize=10M
public class DirectMemory {
public static void main(String[] args) throws IllegalAccessException {
Field declaredField = Unsafe.class.getDeclaredFields()[0];
declaredField.setAccessible(true);
Unsafe unsafe = (Unsafe) declaredField.get(null);
while (true){
unsafe.allocateMemory(1024*1024);
}
}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/347250.html
標籤:其他
上一篇:Docker+jenkins+gitee+springboot實作自動化部署流程(詳細教程)(附下載工具地址)(1)
下一篇:具有編碼能力的可展開結構人造肌肉
