溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析

發(fā)布時間:2021-11-20 14:19:32 來源:億速云 閱讀:148 作者:柒染 欄目:大數(shù)據(jù)

今天就跟大家聊聊有關Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

 

問題初現(xiàn)----電腦雪崩

實驗是這樣的:想測試在指定的棧大?。?60k)下通過不斷創(chuàng)建多線程觀察其造成的 OOM 類型

實驗的代碼如下:

publicclass Test {
private void dontStop() {
while(true) {
}
}

public void stackLeakByThread() {
       while (true) {
           Thread thread = new Thread(new Runnable() {
               @Override public void run() {
                   dontStop();
               }
           });
           thread.start();
       }
   }

   public static  void main(String[] args) {
Test oom = new Test();
oom.stackLeakByThread();
   }
}
 

過了一會兒風扇狂轉,不久就發(fā)生了 OOM,然后程序沒有終止,用 Ctrl + C 也無法終止,會提示「the VM may need to be forcibly terminated」,這是什么鬼,如圖示Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析

電腦卡死了,鼠標鍵盤完全沒法響應!只好重啟了電腦,然后我先在終端輸入 top 命令,再執(zhí)行以上的程序, 發(fā)現(xiàn) CPU 的負載達到了 800%!Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析

在以上對問題的描述中至少有三個問題值得我們?nèi)ニ伎?/p>

  1. 以上 while (true) 為啥會造成 cpu 負載 800%
  2. 在主線程發(fā)生 OOM 后我在終端用 Ctrl + C 試圖終止 Java 進程的執(zhí)行,但沒成功,為啥中止信號不生效呢
  3. 主線程發(fā)生 OOM 后 Java 進程為啥不會停止運行

一個個來看

 

while (true) 與 cpu 負載的關系

首先我們要明白 %CPU 代表的含義,它指的是進程占用一個核的百分比,如果進程啟動了多個線程,多線程就會占用多個核,是可能超過 100% 的,但最多不超過 CPU核數(shù) * 100%, 怎么查看邏輯 CPU 的個數(shù)

  • Linux 下可以用
cat /proc/cpuinfo| grep "processor"| wc -l
 
  • Mac 可以用
sysctl hw.logicalcpu
 

我的電腦是 Mac 的,用以上命令查了一下邏輯核心發(fā)現(xiàn)是 8 個, 而實驗看到的 CPU 占有率是 800%,也就是說我們的實驗程序打滿了 8 個邏輯 CPU!有人說那是因為你在源源不斷地創(chuàng)建線程啊,當然就打滿了所有 CPU 了,那我們再來試驗一下,只創(chuàng)建 7 個線程,加個主線程共 8 個,這 8 個主線程內(nèi)部都只執(zhí)行一個 while(true) ,如下

publicclass Test {
       privateint threadCount = 0;
private void dontStop() {
while(true) {
}
}

public void stackLeakByThread() {
       while (true) {
              // 只創(chuàng)建 7 個線程, 加上主線程共 8 個線程if (threadCount > 7) {
               continue;
           }
           Thread thread = new Thread(new Runnable() {
               @Override public void run() {
                   dontStop();
               }
           });
           thread.start();threadCount++;
       }
   }

   public static  void main(String[] args) {
Test oom = new Test();
oom.stackLeakByThread();
   }
}
 

執(zhí)行之后 %CPU 還是接近 800%(大家可以試驗一下,這里不貼圖了), 也就是說 8 個 while(true) 把 8 個核全部打滿了,平均一個 while(true) 打滿一個核 ,那么問題來了, 單個線程執(zhí)行 while(true) 為啥會打滿一個核呢,CPU 不是按時間片來分配各個進程的嗎

Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析如圖示:操作系統(tǒng)按時間片的調度算法來給不同的進程分配 CPU 時間,如果某個進程時間片用完了,會讓出 CPU 的控制權給其他的進程執(zhí)行

首先,需要指明的是:CPU 確實是按時間片來給不同的進程分配它的控制權的

但 CPU 對時間片的分配策略是動態(tài)的, 具有偏向性的,簡單理解如下:

Java 中的線程執(zhí)行完系統(tǒng)分配的時間片后確實是會讓出 CPU 的執(zhí)行權,但別的進程會告訴系統(tǒng)自己沒什么事情要做,不需要那么多的時間,這個時候系統(tǒng)就會切換到下一個進程,直到回到這個死循環(huán)的進程上,而 Java 進程無論什么時候都再循環(huán),都會一直會報告有事情要做,系統(tǒng)就會把盡可能多的時間分給它(正所謂會哭的小孩有奶吃),系統(tǒng)會不斷調高 while(true) 線程的優(yōu)先級,提升它的 CPU 占用時間片,也就是說 while(true) 這個死循環(huán)用光了別的進程省下的時間,不讓 CPU 有片刻休息的時間,導致 CPU 負載過高,這就像馬太效應,勤奮的線程執(zhí)行的越努力,其他懶惰的線程就越會被縮短時間片,越得不到機會! 

畫外音: Windows 系統(tǒng)中就存在一個稱為「優(yōu)先級推進器」(Priority Boosting,可以關閉)的功能,大致作用就是當系統(tǒng)發(fā)現(xiàn)一個線程執(zhí)行得特別勤奮努力的話,可能會越過線程優(yōu)先級優(yōu)先為此線程分配執(zhí)行時間

 

發(fā)生 OOM 后 Ctrl+C 為啥無法中止 Java 進程

上文提到,發(fā)生 OOM 后, 由于已經(jīng)觀察到 OOM 的現(xiàn)象,所以想把 Java 進程通過 Ctrl+C 殺死,但發(fā)現(xiàn)不起作用,如圖示Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析

為啥 Ctrl + C 這種通用的 kill 掉進程的方式不起作用呢,我在 Oracle 的論壇(見文末參考鏈接)找到了 Oracle 工程師的回答

The message "Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal UNKNOWN to handler- the VM may need to be forcibly terminated" is getting printed by the JVM's native signal handling code. The signal handler itself encountered OOM while making a Java up-call and that's why the JVM didn't get terminated with ctrl+c.

簡單地說就是 JVM 中的信號處理器確實收到了終端發(fā)出的 Ctrl + C 的終止信號,但當它調用 Java 進程想中止時發(fā)生了 OOM 導致中斷失敗, 那為啥調用會發(fā)生 OOM 呢,我猜是因為信號處理器要啟動一個線程來做這種終止通知的操作,而我們知道,當前已經(jīng)無法再創(chuàng)建線程了(已經(jīng)發(fā)生 unable to create new native thread 的錯誤了)

 

主線程發(fā)生 OOM 后 Java 進程為啥不會停止運行

最后一個問題,主線程發(fā)生 OOM 后 Java 進程居然沒終止,這個該怎么解釋

Main 主線程與其他的子線程并不是父子關系,而是平等的關系,所以主線程雖然因為 OOM 掛了,但其他子線程并不會停止運行,由于子線程們執(zhí)行的 while(true),所以子線程會一直存在,既然它們一直存在,那對應的 Java 進程就會一直運行著。

那怎么讓主線程終止運行后,其他線程也可立即結束呢,可以把這些子線程設置為守護線程,創(chuàng)建好 Thread thread 后,可以用 thread.setDaemon(true) 將其設置成守護線程,這樣當主線程掛了,守護線程也會立即停止運行,原因嘛,也很簡單,既然是守護線程,那被守護的線程都掛了,那守護線程也沒存在的意義了

通過一個 OOM 試驗引出了三個值得思考的問題,相信大家應該學了不少知識點,這里還是要提醒一下大家,看到書中的 demo 時,最好能親自去嘗試一下,說不定你能有新的發(fā)現(xiàn)!紙上得來終覺淺,絕知此事要躬行!

看完上述內(nèi)容,你們對Java中OOM試驗造成的電腦雪崩引發(fā)的示例分析有進一步的了解嗎?如果還想了解更多知識或者相關內(nèi)容,請關注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI