某團面試題:JVM 堆內存溢出後,其他線程是否可繼續工作?

2021-12-28 程式設計師解析

設為「星標」,重磅乾貨,第一時間送達

最近網上出現一個美團面試題:「一個線程OOM後,其他線程還能運行嗎?」

我看網上出現了很多不靠譜的答案。這道題其實很有難度,涉及的知識點有jvm內存分配、作用域、gc等,不是簡單的是與否的問題。

由於題目中給出的OOM,java中OOM又分很多類型;比如:堆溢出(「java.lang.OutOfMemoryError: Java heap space」)、永久帶溢出(「java.lang.OutOfMemoryError:Permgen space」)、不能創建線程(「java.lang.OutOfMemoryError:Unable to create new native thread」)等很多種情況。

本文主要是分析堆溢出對應用帶來的影響。

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

代碼如下:

publicclass JvmThread {

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 = newbyte[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();
   }
}

結果展示:

Wed Nov 07 14:42:18 CST 2018Thread[Thread-1,5,main]==
Wed Nov 07 14:42:18 CST 2018Thread[Thread-0,5,main]==
Wed Nov 07 14:42:19 CST 2018Thread[Thread-1,5,main]==
Wed Nov 07 14:42:19 CST 2018Thread[Thread-0,5,main]==
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at com.gosaint.util.JvmThread.lambda$main$0(JvmThread.java:21)
at com.gosaint.util.JvmThread$$Lambda$1/521645586.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Wed Nov 07 14:42:20 CST 2018Thread[Thread-1,5,main]==
Wed Nov 07 14:42:21 CST 2018Thread[Thread-1,5,main]==
Wed Nov 07 14:42:22 CST 2018Thread[Thread-1,5,main]==

JVM啟動參數設置:

上圖是JVM堆空間的變化。我們仔細觀察一下在14:42:05~14:42:25之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表這一點,當一個線程拋出OOM異常後,它所佔據的內存資源會全部被釋放掉,從而不會影響其他線程的運行!

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

總結:其實發生OOM的線程一般情況下會死亡,也就是會被終結掉,該線程持有的對象佔用的heap都會被gc了,釋放內存。因為發生OOM之前要進行gc,就算其他線程能夠正常工作,也會因為頻繁gc產生較大的影響。

更多精彩內容,關注我們

▼▼

點個「在看」吧

相關焦點

  • 「JVM教程與調優」了解JVM 堆內存溢出以及非堆內存溢出
    非堆內存溢出演示接下來我們來演示一下非堆內存溢出,我們繼續沿用上方代碼。非堆內存主要是MataSpace,那麼我們如何構建一個非堆內存溢出呢?>非堆內存溢出的演示。java.lang.IncompatibleClassChangeError: Found interface org.objectweb.asm.MethodVisitor, but class was expected這個異常另外寫在java.lang.IncompatibleClassChangeError,小夥伴如果有遇到,可嘗試一下是否能夠解決
  • java線程前傳——jvm內存結構、內存模型和cpu結構
    ,這個過程是少不了的一個線程肯定是要運行在一個核上的,多個線程可以運行在不同的核上,這個時候,因為緩存的存在,如果沒有同步機制,那一個線程修改了緩存的數據,另一個線程也修改了緩存的數據,這個時候這兩個線程修改後的數據都需要寫入到內存當中,就會出現問題jvm為了方便,將這些緩存抽象出來,構造了自己的內存模型,即主內存和工作內存的數據交互,即java 內存模型(jmm)
  • JVM內存區域之線程私有區域
    對於java程式設計師來說,在虛擬機自動內存管理機制的幫助下,不再需要為沒一個new操作去配對的free/delete(C、C++語言對對象的刪除和內存釋放操作),不容易出現內存洩漏和內存溢出問題,看起來由虛擬機管理內存一切看起來很美好。
  • jvm面試系列一:java內存模型你掌握了多少?
    如果你應聘的職位涉及系統調優,如堆大小的分配、垃圾回收機制的選擇、處理內存溢出、線程死鎖等,對JVM這一塊知識就有更高的要求。說明:因為知識點較多,擴展開來篇幅太長,jvm系列面試題將會分解開來從內存模型,垃圾回收,類加載機制,參數調優等多個角度整理,方便大家閱讀。
  • JVM超神之路:年後跳槽需要的JVM知識點,周末給你整理了一份!!!
    1、Java內存結構2、對象創建時堆內存分配算法3、對象在內存中的存儲布局4、對象怎麼定位5、判斷對象是否能被回收的算法6、如何判斷對象是否能被回收7、Java堆內存組成部分8、什麼時候拋出StackOverflowError9、Java中會存在內存洩漏嗎,請簡單描述。
  • 精美圖文帶你掌握JVM 內存布局
    本文主題內容:JVM 內存區域概覽堆區的空間分配是怎麼樣?堆溢出的演示創建一個新對象內存是怎麼分配的?方法區 到 Metaspace 元空間棧幀是什麼?棧幀裡有什麼?怎麼理解?本地方法棧程序計數器Code Cache 是什麼?
  • 優雅終止線程?系統內存佔用較高?
    那麼,現在得到一個初步的結論就是:不管是該jvm進程用到的堆內存還是堆外內存,都很小(相對於top命令顯式的18% * 8G佔用量而言)。所以是否可以猜想:jvm只是向作業系統申請了這麼多內存暫時沒有歸還回去,留待下次線程池有新任務時繼續復用呢?本文最後一部分試驗就圍繞著一點展開。
  • JVM 面試題解答(40道全)
    7、JVM 的內存模型是什麼?JVM 試圖定義一種統一的內存模型,能將各種底層硬體以及作業系統的內存訪問差異進行封裝,使 Java 程序在不同硬體以及作業系統上都能達到相同的並發效果。它分為工作內存和主內存,線程無法對主存儲器直接進行操作,如果一個線程要和另外一個線程通信,那麼只能通過主存進行交換。8、JVM 如何確定垃圾對象?
  • JVM知識點精華匯總
    我們可以這樣理解,虛擬機中可以供棧佔用的空間≈可用物理內存 - 最大堆內存 - 最大方法區內存,比如一臺機器內存為4G,系統和其他應用佔用2G,虛擬機可用的物理內存為2G,最大堆內存為1G,最大方法區內存為512M,那可供棧佔有的內存大約就是512M,假如我們設置每個線程棧的大小為1M,那虛擬機中最多可以創建512個線程,超過512個線程再創建就沒有空間可以給棧了,就報OutOfMemoryError
  • JVM性能調優
    ②堆棧錯誤信息:當系統出現異常後,可以根據堆棧信息初步定位問題所在,比如根據「java.lang.OutOfMemoryError: Java heap space」可以判斷是堆內存溢出;根據「java.lang.StackOverflowError」可以判斷是棧溢出;根據「java.lang.OutOfMemoryError: PermGen space」可以判斷是方法區溢出等。
  • Java底層深入-JVM調優
    以上這些面試題是否在面試過程中有被問到呢? 一. JVM中的內存洩露和內存溢出- 內存洩露:      內存洩漏是指本應該被GC回收的無用對象沒有被回收,導致的內存空間的浪費 。當內存洩露嚴重時會導致OOM。
  • JVM性能調優實踐
    ②堆棧錯誤信息:當系統出現異常後,可以根據堆棧信息初步定位問題所在,比如根據「java.lang.OutOfMemoryError: Java heap space」可以判斷是堆內存溢出;根據「java.lang.StackOverflowError」可以判斷是棧溢出;根據「java.lang.OutOfMemoryError: PermGen space」可以判斷是方法區溢出等。
  • 13道 常見的JVM 面試題
    主內存:java虛擬機規定所有的變量(不是程序中的變量)都必須在主內存中產生,為了方便理解,可以認為是堆區。工作內存:java虛擬機中每個線程都有自己的工作內存,該內存是線程私有的為了方便理解,可以認為是虛擬機棧。
  • 線上報了內存溢出異常,又不完全是內存溢出
    (一)前言最近一直忙於對付即將上線的系統,期間也碰到了很多問題。
  • JVM中的五大內存區域劃分詳解及快速掃盲
    簡單用一張圖來理解這三個的關係:3. jvm的組成成分不了解jvm的同學看到這張圖後可能會有點懵逼,不過沒關係,放這張圖只是想讓你了解jvm中有三塊內容非常重要,1.java代碼如何執行?Java堆Java堆是java虛擬機所管理的內存中最大的一塊,是被所有線程都共享的內存區域。存在的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裡進行分配內存。不過目前隨著技術的不斷發展,也並不是所有的對象實例都在堆中分配內存,可能也存在棧上分配。
  • JVM-概述和內存區域
    方法區和堆區是所有線程共享的內存區域;Java棧又叫做jvm虛擬機棧。執行引擎等同於翻譯class文件的語言翻譯器。方法區(永久代)在jdk8中又叫做元空間Metaspace運行時數據區概述堆內存:保存所有引用數據的真實信息;棧內存:基本類型、運算、指向堆內存的指針;方法區:所以定義的方法的信息都保存方法區中,屬於共享區
  • Android避免內存溢出(Out of Memory)方法總結
    絕對乾貨-國內值得關注的官方API集合,很全很強大(必須收藏)[乾貨]2017已來,最全面試總結——這些Android面試題你一定需要避免內存溢出的方法,主要是對以下三個方面對程序進行優化在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間是否足夠,都會回收它的內存。 不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。 弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
  • 想理解JVM看了這篇文章,就知道了!
    本次將對jvm有更深入的學習,我們不僅要讓程序能跑起來,而且是可以跑的更快!可以分析解決在生產環境中所遇到的各種「棘手」的問題,比如運行的應用卡住了,日誌不輸出,程序沒有反應,CPU負載突然升高,多線程應用下,如何分配線程數量等。
  • 淺析JVM內存模型:虛擬機如何實現多線程而導致的並發問題
    但對 於實例對象的成員變量,不管它是基本數據類型或者包裝類型(Integer、Double等)還是引用類型,都會被存儲到 堆區。至於static變量以及類本身相關信息將會存儲在主內存中。需要注意的是,在主內存中的實例對象可以被多 線程共享,倘若兩個線程同時調用了同一個對象的同一個方法,那麼兩條線程會將要操作的數據拷貝一份到自己的 工作內存中,執行完成操作後才刷新到主內存。
  • JVM 初探:內存分配、GC 原理與垃圾收集器
    何時回收-對象生死判定(哪些內存需要回收/何時回收)在堆裡面存放著Java世界中幾乎所有的對象實例, 垃圾收集器在對堆進行回收前, 第一件事就是判斷哪些對象已死(可回收).回收廢棄常量與回收其他年代中的對象類似, 但要判斷一個類是否無用則條件相當苛刻:該類所有的實例都已經被回收, Java堆中不存在該類的任何實例;該類對應的Class對象沒有在任何地方被引用(也就是在任何地方都無法通過反射訪問該類的方法);加載該類的ClassLoader已經被回收.