放假這幾天,溫習了深刻理解Java虛擬機的第二章, 整理了JVM發生OOM異常的幾種狀況,並分析緣由以及解決方案,但願對你們有幫助。html
Java堆用於存儲對象實例,只要不斷地建立對象,而且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆的容量限制後就會產生內存溢出異常。java
java.lang.OutOfMemoryError: Java heap space
複製代碼
package oom;
import java.util.ArrayList;
import java.util.List;
/**
* JVM配置參數
* -Xms20m JVM初始分配的內存20m
* -Xmx20m JVM最大可用內存爲20m
* -XX:+HeapDumpOnOutOfMemoryError 當JVM發生OOM時,自動生成DUMP文件
* -XX:HeapDumpPath=/Users/weihuaxiao/Desktop/dump/ 生成DUMP文件的路徑
*/
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
//在堆中無限建立對象
while (true) {
list.add(new OOMObject());
}
}
}
複製代碼
按照前面的排查解決方案,咱們來一波分析。spring
1.查找報錯關鍵信息bash
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
複製代碼
2. 使用內存映像分析工具Jprofiler分析產生的堆儲存快照jsp
由圖可得,OOMObject這個類建立了810326個實例,是屬於內存溢出,這時候先定位到對應代碼,發現死循環致使的,修復便可。函數
關於虛擬機棧和本地方法棧,在Java虛擬機規範中描述了兩種異常:工具
package oom;
/**
* -Xss2M
*/
public class JavaVMStackOOM {
private void dontStop(){
while(true){
}
}
public void stackLeakByThread(){
while(true){
Thread thread = new Thread(new Runnable(){
public void run() {
dontStop();
}
});
thread.start();}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
複製代碼
1.查找報錯關鍵信息post
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
複製代碼
2.肯定是建立線程致使的棧溢出OOM性能
Thread thread = new Thread(new Runnable(){
public void run() {
dontStop();
}
});
複製代碼
3.排查代碼,肯定是否顯示使用死循環建立線程,或者隱式調用第三方接口建立線程(以前公司,調用騰訊雲第三方接口,上傳圖片,遇到這個問題)學習
方法區,(又叫永久代,JDK8後,元空間替換了永久代),用於存放Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。運行時產生大量的類,會填滿方法區,形成溢出。
package oom;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* jdk8以上的話,
* 虛擬機參數:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
}
複製代碼
1.查找報錯關鍵信息
Caused by: java.lang.OutOfMemoryError: Metaspace
複製代碼
2.檢查JVM元空間設置參數是否太小
-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
複製代碼
3. 檢查對應代碼,是否使用CGLib生成了大量的代理類
while (true) {
...
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
複製代碼
直接內存並非虛擬機運行時數據區的一部分,也不是Java 虛擬機規範中定義的內存區域。可是,這部份內存也被頻繁地使用,並且也可能致使OOM。
在JDK1.4 中新加入了NIO(New Input/Output)類,它可使用 native 函數庫直接分配堆外內存,而後經過一個存儲在Java堆中的 DirectByteBuffer 對象做爲這塊內存的引用進行操做。這樣能在一些場景中顯著提升性能,由於避免了在 Java 堆和 Native 堆中來回複製數據。
package oom;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
/**
* -Xmx256m -XX:MaxDirectMemorySize=100M
*/
public class DirectByteBufferTest {
public static void main(String[] args) throws InterruptedException{
//分配128MB直接內存
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
TimeUnit.SECONDS.sleep(10);
System.out.println("ok");
}
}
複製代碼
ByteBuffer分配128MB直接內存,而JVM參數-XX:MaxDirectMemorySize=100M指定最大是100M,所以發生直接內存溢出。
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
複製代碼
package oom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* JVm參數 -Xmx8m -Xms8m
*/
public class GCoverheadTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
});
}
}
}
複製代碼
本文介紹瞭如下幾種常見OOM異常
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: unable to create new native thread
java.lang.OutOfMemoryError: Metaspace
java.lang.OutOfMemoryError: Direct buffer memory
java.lang.OutOfMemoryError: GC overhead limit exceeded
複製代碼
但願你們遇到OOM異常時,對症下藥,順利解決問題。同時,若是有哪裏寫得不對,歡迎指出,感激涕零。