這題是一個網友
@大臉貓愛吃魚
給我的提問,出自今年校招
美團三面
的一個真題。大緻如下:
一個程序有3個線程,如果一個線程抛出oom,其他兩個線程還能運作麼?
先說一下答案,答案是還
能運作
。
不瞞大家說,真在面試中,我遇到這一題,我估計也是答錯。因為我初看到這一題,内心嘿嘿一笑,覺得這題是在考察JVM的記憶體結構。我第一反應是OOM的常見情況堆溢出,也就是下面的這種異常:
java.lang.OutOfMemoryError: Java heap space
先回憶一下,多線程中棧與堆是公有的還是私有的?回答如下:
在多線程環境下,每個線程擁有一個棧和一個程式計數器。 棧和程式計數器用來儲存線程的執行曆史和線程的執行狀态,是線程私有的資源。其他的資源(比如堆、位址空間、全局變量)是由同一個程序内的多個線程共享。
也就是說,堆是線程共享。那麼一個線程堆抛出OOM異常,我第一反應是另外兩個線程也抛出OOM異常,畢竟堆是共有的,大家應該都抛出異常。于是,我機智的讓
@大臉貓愛吃魚
寫個代碼去測試一下,結果我被啪啪啪打臉了。
測試代碼僞如下:
一個線程去構造堆溢出,每隔1S申請一次堆,代碼長下面這樣:
new Thread(() -> {
List<byte[]> list=new ArrayList<byte[]>();
while(true){
System.out.println(new Date().toString()+Thread.currentThread()+"==");
byte[] b = new byte[1024*1024*1];
list.add(b);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
另一個線程,睡眠1秒然後輸出就好,代碼長下面這樣:
new Thread(() -> {
while(true){
System.out.println(new Date().toString()+Thread.currentThread()+"==");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
結果,輸出是長下面這樣的:
Fri Sep 21 11:18:39 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:39 CST 2018Thread[Thread-0,5,main]==
Fri Sep 21 11:18:40 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:40 CST 2018Thread[Thread-0,5,main]==
Fri Sep 21 11:18:41 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:41 CST 2018Thread[Thread-0,5,main]==
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at com.lpf.TestOOM.lambda$0(TestOOM.java:15)
at com.lpf.TestOOM$$Lambda$1/531885035.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Fri Sep 21 11:18:42 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:43 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:44 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:45 CST 2018Thread[Thread-1,5,main]==
Fri Sep 21 11:18:46 CST 2018Thread[Thread-1,5,main]==
大家發現了麼,一個線程溢出了,其他線程還在跑,這好像和我們的認知不大一樣。坦白說,我看到這個結果,瞬間覺得自己一世英名毀于一旦,從此無法擡起頭來做人。沒辦法了,隻能亮出工具來看一下了。
先說一下,在本例測試中,參數如下:
-Xms16m -Xmx32m
-Xms 初始堆記憶體
-Xmx 最大堆記憶體
接下來,亮出
JvisualVM
看堆的變化,注意看上面那張圖,抛出OOM的時間約在00:11:45左右,是以我們需要重點關注00:11:45左右的曲線變化,如下圖所示:
如圖所示,我們仔細觀察一下在14:06:20~14:06:21之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表這一點,當一個線程抛出OOM異常後,它所占據的記憶體資源會全部被釋放掉,進而不會影響其他線程的運作!
講到這裡大家應該懂了,此題的答案為
一個線程溢出後,程序裡的其他線程還能照常運作
。注意了,這個例子我隻示範了堆溢出的情況。如果是棧溢出,結論也是一樣的,大家可自行通過代碼測試。
完整代碼
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TestOOM {
public static void main(String[] args) {
// 線程一
new Thread(() -> {
List<byte[]> list = new ArrayList<byte[]>();
while (true) {
System.out.println(new Date().toString() + Thread.currentThread() + "==");
byte[] b = new byte[1024 * 1024 * 1];
list.add(b);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// 線程二
new Thread(() -> {
while (true) {
System.out.println(new Date().toString() + Thread.currentThread() + "==");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
運作時設定記憶體參數