【原創】一個線程oom,進程里其他線程還能運行嗎?


引言

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

一個進程有3個線程,如果一個線程拋出oom,其他兩個線程還能運行么?

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

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

java.lang.OutOfMemoryError: Java heap space

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

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

也就是說,堆是線程共享。那么一個線程堆拋出OOM異常,我第一反應是另外兩個線程也拋出OOM異常,畢竟堆是共有的,大家應該都拋出異常。於是,我機智的讓@大臉貓愛吃魚寫個代碼去測試一下,結果我被啪啪啪打臉了。
測試代碼偽如下
一個線程去構造堆溢出,每隔1秒申請一次堆,代碼長下面這樣

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();

結果,輸出是長下面這樣的
image
大家發現了么,一個線程溢出了,其他線程還在跑,這好像和我們的認知不大一樣。坦白說,我看到這個結果,瞬間覺得自己一世英名毀於一旦,從此無法抬起頭來做人。沒辦法了,只能亮出工具來看一下了。
先說一下,在本例測試中,參數如下

-Xms16m -Xmx32m
-Xms 初始堆內存 
-Xmx 最大堆內存

接下來,亮出JvisualVM看堆的變化,注意看上面那張圖,拋出OOM的時間約在00:11:45 左右,因此我們需要重點關注00:11:45左右的曲線變化,如下圖所示
image
如圖所示,我們仔細觀察一下在00:11:44~00:11:45之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表着一點,當一個線程拋出OOM異常后,它所占據的內存資源會全部被釋放掉,從而不會影響其他線程的運行!

講到這里大家應該懂了,此題的答案為一個線程溢出后,進程里的其他線程還能照常運行。注意了,這個例子我只演示了堆溢出的情況。如果是棧溢出,結論也是一樣的,大家可自行通過代碼測試。

說時遲,那時快。一個機智的網友又給我提了一個問題?

如果主線程拋異常退出了,子線程還能運行么?

ok,這個問題要從子線程和主線程的關系講起。
先來一個定義

線程不像進程,一個進程中的線程之間是沒有父子之分的,都是平級關系。即線程都是一樣的, 退出了一個不會影響另外一個。

因此,答案是如果主線程拋異常退出了,子線程還能運行。
但是有一個例外情況,如果這些子線程都是守護線程,那么子線程會隨着主線程結束而結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM