內存溢出
內存溢出指的是內存中某一塊區域的使用量超過了允許使用的最大值,從而使用內存時因空間不足而失敗,虛擬機一般會拋出指定的錯誤。
在Java虛擬機中,只有程序計數器不會出現內存溢出的情況,因為每個線程的程序計數器只保存一個固定長度的地址。
堆內存溢出
(java.lang.OutOfMemoryError: Java heap space
)
? 堆內存溢出指的是在堆上分配的對象空間超過了堆的最大大小,從而導致的內存溢出。堆的最大大小使用-Xmx參數進行設置,如-Xmx10m代表最大堆內存大小為10m。
? Java堆用于儲存對象實例,我們只要不斷地創建對象,并且保證GC Roots到對象之間有可達路徑 來避免垃圾回收機制清除這些對象,那么隨著對象數量的增加,總容量觸及最大堆的容量限制后就會 產生內存溢出異常。
棧內存溢出
(java.lang.OutOfMemoryError: unable to create new native thread
)
? 棧內存溢出指的是所有棧幀空間的占用內存超過了最大值,最大值使用-Xss進行設置,比如-Xss256k代表所有棧幀占用內存大小加起來不能超過256k。
方法區內存溢出
(java.lang.OutOfMemoryError: Metaspace
/ PermGen space
)
方法區內存溢出指的是方法區中存放的內容比如類的元信息超過了方法區內存的最大值,JDK7及之前版本方法區使用永久代(-XX:MaxPermSize=值)來實現,JDK8及之后使用元空間(-XX:MaxMetaspaceSize=值)來實現。
原因:加載的類過多或動態生成類(如反射、CGLIB),超出元空間限制(-XX:MaxMetaspaceSize
)。
import javassist.ClassPool;public class MetaspaceOOM {public static void main(String[] args) throws Exception {ClassPool cp = ClassPool.getDefault();for (int i = 0; i < 100000; i++) {// 動態生成類Class<?> clazz = cp.makeClass("MetaspaceOOM" + i).toClass();}}
}
直接內存溢出
(java.lang.OutOfMemoryError: Direct buffer memory
)
? 直接內存溢出指的是申請的直接內存空間大小超過了最大值,使用 -XX:MaxDirectMemorySize=值 設置最大值。溢出之后會拋出OutOfMemoryError:
總結:
OOM 類型 | 觸發原因 | 解決方案 |
---|---|---|
堆內存溢出 | 對象過多/內存泄漏 | 調整 -Xmx ,優化代碼 |
元空間溢出 | 動態類過多 | 限制 -XX:MaxMetaspaceSize |
棧溢出(線程數過多) | 線程數超出系統限制 | 使用線程池,調整 -Xss |
直接內存溢出 | NIO 分配過多直接內存 | 調整 -XX:MaxDirectMemorySize |
- 通過
jstat
、jmap
、jstack
監控內存使用。 - 生成堆轉儲文件(
-XX:+HeapDumpOnOutOfMemoryError
),用 MAT 分析內存泄漏。 - 避免過度依賴反射、動態代理等易觸發元空間問題的技術。
如何避免 OOM?
1. 堆內存溢出
? 診斷工具:
? ? 使用 jvisualvm
、MAT
(Memory Analyzer Tool)分析堆轉儲(-XX:+HeapDumpOnOutOfMemoryError
)。
? ? 檢查是否有內存泄漏(對象被意外長期引用)。
? ? 優化方法:
? ? 調整堆大小(-Xmx
和 -Xms
)。
? ? 優化代碼,及時釋放無用對象(如清理集合、關閉資源)。
? ? 避免創建超大對象(如大數組)。
2. 元空間溢出
? 診斷工具:
? ? 使用 jstat -gcmetacapacity
監控元空間使用情況。
? ? 檢查動態生成類的代碼(如反射、動態代理)。
? ? 優化方法:
? ? 限制元空間大小(-XX:MaxMetaspaceSize=256m
)。
? ? 減少動態類生成(如緩存反射生成的類)。
3. 棧溢出(線程數過多)
? 診斷工具:
? ? 檢查線程數(ps -eLf | grep java
)。
? ? 分析線程棧(jstack
)。
? ? 優化方法:
? ? 減少線程數(使用線程池)。
? ? 調整線程棧大小(-Xss256k
)。
4. 直接內存溢出
? 診斷工具:
? ? 監控 java.nio.Bits
的 reservedMemory
(NIO 內存使用)。
? ? 優化方法:
? ? 顯式釋放直接內存(調用 ((DirectBuffer) buffer).cleaner().clean()
)。
? ? 調整 -XX:MaxDirectMemorySize
。