java之OOM排查

1.引子

今天聊一下OOM的問題。OOM就是Out Of Memory。前幾天,線上出現過一次,頻繁的full GC的問題。今天就簡單記錄一下排查的步驟。

2.模擬

在這隻能模擬OOM。很簡單,就是一個集合,寫個循環向裡面添加對象。這個方法:startThread2

<code>import java.util.ArrayList;
import java.util.List;
class Test {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("運行的時候請輸入參:1 or 2");
return;
}
if (args[0].equalsIgnoreCase("1")) {
startThread1();
}
if (args[0].equalsIgnoreCase("2")) {
startThread2();
}
}
public static void startThread1() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
String a = new String("deadThread");
\t\t\t\t\t//System.out.println(a);
}
}
});
thread.setName("deadThread");
thread.start();
}
public static void startThread2() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
List<string> list = new ArrayList<>();
while (true) {
String a1 = new String("OOMThread");\t\t\t\t\t
list.add(a1);\t\t\t\t\t
}
}
});
thread.setName("OOMThread");
thread.start();

}
}/<string>/<code>

3.編譯運行

<code>javac test.java/<code>


java之OOM排查

javac test.java


<code>java -Xms10m -Xmx10m -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ Test 2/<code>


java之OOM排查

注:

-Xms10m -Xmx10m 初始堆大小和最大堆大小。 這裡是模擬,所以設置小一點

-XX:+PrintGC -XX:+PrintGCDetails 打印一下GC的日誌

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ 當發送OOM的時候,自動保存Dump文件。後面那個是報錯的路徑,如果沒有則,在當前目錄下。

已運行會一直打gc日誌,因為死循環在添加對象,並且對象還沒法回收。

一會就會內存溢出,如下圖


java之OOM排查

4分析dump文件

發生OOM了。接著就是要分析一下這個dump文件了。分析的工具有很多,這裡使用mat和jvisualvm分別分析一下,其實都差不多。


java之OOM排查

首先,從服務器下載到本地。然後使用工具就行分析。

1.首先使用MAT分析:

打開mat,導入hprof文件。(注:如果hprof文件特別的大,需要調整一下mat的內存參數。防止內存溢出,打不開文件)


java之OOM排查

mat


java之OOM排查


java之OOM排查


java之OOM排查

我們可以看到mat已經幫我們分析到了一個錯誤:


java之OOM排查

點擊details詳情。會發現:

線程 OOMThread中一個ArrayList佔了98.12%,而它裡面放的是String對象。

java之OOM排查

注:這裡面有兩個比較難理解的名詞(我理解的不一定完全對。大家可以自行百度一下):

1.shallow heap:shallow:淺的,我理解就是:這個對象實際佔用的堆大小

2.retained heap:retained:保留的,這個有點難理解了:簡單的話,就是它和它引用的對象(這裡的引用對象沒有被其他的對象引用)的佔用堆的大小。(不知道理解的對不對。)如下圖:arrayList中shallow是24,retained是9057600.以為我newString的都放到list中去了。


java之OOM排查

初步懷疑是ArrayList中對象過多,一直有引用,沒有被gc回收導致。然後看一下拋出OOM的異常的地方。發現有對應的ArrayList。它有一個循環在一直添加String對象。

2.使用jvisualvm.exe

打開jvisualvm.exe,導入hprof文件。


java之OOM排查

在出現 OutOfMemoryError 異常錯誤時進行了堆轉儲導致 OutOfMemoryError 異常錯誤的線程: OOMThread

java之OOM排查

點擊:OOMThread


java之OOM排查

顯示是在什麼地方發生的OOM,然後點擊 紅框的地方。

java之OOM排查

我們可以發現:arrayList的size大小為:317374. 猜測是arrayList結合中對象太多。由此可以去查對應的代碼邏輯判斷。

還可以點擊類:會顯示對象的個數和佔比。會發現string對象,有318309個實例,佔所有實例中99.1%,大小是8912652,佔用了堆74.2%的空間


java之OOM排查


注:實際生產中情況,比這個要複雜,不過大概分析過程就是這樣。其實頻繁的fullgc也可以導出dump文件去分析。是不是有內存洩漏的地方。當然,一般大公司會有相應的監控系統,在堆空間超過某個閾值就會報警,這個時候,可以將這臺機器從vip摘除或者(healthcheck先摘除了)這樣Nginx就打不過來,就不會有用戶的請求過來了。然後使用jmap導出dump文件分析。這個以後有時間再講。


分享到:


相關文章: