點選上方“Java技術驛站”,選擇“置頂公眾號”。
有內涵、有價值的文章第一時間送達!
精品專欄
這題是一個網友 @大臉貓愛吃魚
給我的提問,出自今年校招美團三面的一個真題。大致如下
一個行程有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();
結果,輸出是長下麵這樣的
大家發現了麼,一個執行緒上限溢位了,其他執行緒還在跑,這好像和我們的認知不大一樣。坦白說,我看到這個結果,瞬間覺得自己一世英名毀於一旦,從此無法抬起頭來做人。沒辦法了,只能亮出工具來看一下了。 先說一下,在本例測試中,引數如下 -Xms16m -Xmx32m -Xms 初始堆記憶體 -Xmx 最大堆記憶體
接下來,亮出 JvisualVM
看堆的變化,註意看上面那張圖,丟擲OOM的時間約在 00:11:45
左右,因此我們需要重點關註 00:11:45
左右的曲線變化,如下圖所示
如圖所示,我們仔細觀察一下在 00:11:44
~ 00:11:45
之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表著一點,當一個執行緒丟擲OOM異常後,它所佔據的記憶體資源會全部被釋放掉,從而不會影響其他執行緒的執行! 講到這裡大家應該懂了,此題的答案為一個執行緒上限溢位後,行程裡的其他執行緒還能照常執行。註意了,這個例子我只演示了堆上限溢位的情況。如果是棧上限溢位,結論也是一樣的,大家可自行透過程式碼測試。
補充:OOM發生時如果程式能正常處理這個異常情況,比如不再申請更多的記憶體或其它資源,或者放棄那個子任務或子執行緒,系統OOM狀態是可以回到正常情況
END