歡迎光臨
每天分享高質量文章

美團三面:一個執行緒OOM,行程裡其他執行緒還能執行麼?

點選上方“Java技術驛站”,選擇“置頂公眾號”。

有內涵、有價值的文章第一時間送達!

精品專欄

 

這題是一個網友 @大臉貓愛吃魚給我的提問,出自今年校招美團三面的一個真題。大致如下

一個行程有3個執行緒,如果一個執行緒丟擲oom,其他兩個執行緒還能執行麼?

先說一下答案,答案是還能執行

不瞞大家說,真在面試中,我遇到這一題,我估計也是答錯。因為我初看到這一題,內心嘿嘿一笑,覺得這題是在考察JVM的記憶體結構。我第一反應是OOM的常見情況堆上限溢位,也就是下麵的這種異常

  1. java.lang.OutOfMemoryError: Java heap space

先回憶一下,多執行緒中棧與堆是公有的還是私有的?回答如下

在多執行緒環境下,每個執行緒擁有一個棧和一個程式計數器。棧和程式計數器用來儲存執行緒的執行歷史和執行緒的執行狀態,是執行緒私有的資源。其他的資源(比如堆、地址空間、全域性變數)是由同一個行程內的多個執行緒共享。

也就是說,堆是執行緒共享。那麼一個執行緒堆丟擲OOM異常,我第一反應是另外兩個執行緒也丟擲OOM異常,畢竟堆是共有的,大家應該都丟擲異常。於是,我機智的讓 @大臉貓愛吃魚寫個程式碼去測試一下,結果我被啪啪啪打臉了。 測試程式碼偽如下 一個執行緒去構造堆上限溢位,每隔1S申請一次堆,程式碼長下麵這樣

  1. new Thread(() -> {

  2.     List<byte[]> list=new ArrayList<byte[]>();

  3.     while(true){

  4.         System.out.println(new Date().toString()+Thread.currentThread()+"==");

  5.         byte[] b = new byte[1024*1024*1];

  6.         list.add(b);

  7.         try {

  8.             Thread.sleep(1000);

  9.         } catch (Exception e) {

  10.             e.printStackTrace();

  11.         }

  12.     }

  13. }).start();

另一個執行緒,睡眠1秒然後輸出就好,程式碼長下麵這樣

  1. new Thread(() -> {

  2.     while(true){

  3.         System.out.println(new Date().toString()+Thread.currentThread()+"==");

  4.         try {

  5.             Thread.sleep(1000);

  6.         } catch (Exception e) {

  7.             e.printStackTrace();

  8.         }

  9.     }

  10. }).start();

結果,輸出是長下麵這樣的

大家發現了麼,一個執行緒上限溢位了,其他執行緒還在跑,這好像和我們的認知不大一樣。坦白說,我看到這個結果,瞬間覺得自己一世英名毀於一旦,從此無法抬起頭來做人。沒辦法了,只能亮出工具來看一下了。 先說一下,在本例測試中,引數如下 -Xms16m -Xmx32m -Xms 初始堆記憶體  -Xmx 最大堆記憶體

接下來,亮出 JvisualVM看堆的變化,註意看上面那張圖,丟擲OOM的時間約在 00:11:45左右,因此我們需要重點關註 00:11:45左右的曲線變化,如下圖所示

如圖所示,我們仔細觀察一下在 00:11:44~ 00:11:45之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表著一點,當一個執行緒丟擲OOM異常後,它所佔據的記憶體資源會全部被釋放掉,從而不會影響其他執行緒的執行! 講到這裡大家應該懂了,此題的答案為一個執行緒上限溢位後,行程裡的其他執行緒還能照常執行。註意了,這個例子我只演示了堆上限溢位的情況。如果是棧上限溢位,結論也是一樣的,大家可自行透過程式碼測試。

補充:OOM發生時如果程式能正常處理這個異常情況,比如不再申請更多的記憶體或其它資源,或者放棄那個子任務或子執行緒,系統OOM狀態是可以回到正常情況

END

贊(0)

分享創造快樂