今天就跟大家聊聊有關(guān)Java中OOM試驗(yàn)造成的電腦雪崩引發(fā)的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
網(wǎng)站建設(shè)公司,為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁設(shè)計(jì)及定制網(wǎng)站建設(shè)服務(wù),專注于成都定制網(wǎng)站,高端網(wǎng)頁制作,對社區(qū)文化墻等多個(gè)行業(yè)擁有豐富的網(wǎng)站建設(shè)經(jīng)驗(yàn)的網(wǎng)站建設(shè)公司。專業(yè)網(wǎng)站設(shè)計(jì),網(wǎng)站優(yōu)化推廣哪家好,專業(yè)seo優(yōu)化排名優(yōu)化,H5建站,響應(yīng)式網(wǎng)站。
實(shí)驗(yàn)是這樣的:想測試在指定的棧大?。?60k)下通過不斷創(chuàng)建多線程觀察其造成的 OOM 類型
實(shí)驗(yàn)的代碼如下:
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ēng)扇狂轉(zhuǎn),不久就發(fā)生了 OOM,然后程序沒有終止,用 Ctrl + C 也無法終止,會提示「the VM may need to be forcibly terminated」,這是什么鬼,如圖示
電腦卡死了,鼠標(biāo)鍵盤完全沒法響應(yīng)!只好重啟了電腦,然后我先在終端輸入 top 命令,再執(zhí)行以上的程序, 發(fā)現(xiàn) CPU 的負(fù)載達(dá)到了 800%!
在以上對問題的描述中至少有三個(gè)問題值得我們?nèi)ニ伎?/p>
一個(gè)個(gè)來看
首先我們要明白 %CPU代表的含義,它指的是進(jìn)程占用一個(gè)核的百分比,如果進(jìn)程啟動了多個(gè)線程,多線程就會占用多個(gè)核,是可能超過 100% 的,但最多不超過 CPU核數(shù) * 100%, 怎么查看邏輯 CPU 的個(gè)數(shù)
cat /proc/cpuinfo| grep "processor"| wc -l
sysctl hw.logicalcpu
我的電腦是 Mac 的,用以上命令查了一下邏輯核心發(fā)現(xiàn)是 8 個(gè), 而實(shí)驗(yàn)看到的 CPU 占有率是 800%,也就是說我們的實(shí)驗(yàn)程序打滿了 8 個(gè)邏輯 CPU!有人說那是因?yàn)槟阍谠丛床粩嗟貏?chuàng)建線程啊,當(dāng)然就打滿了所有 CPU 了,那我們再來試驗(yàn)一下,只創(chuàng)建 7 個(gè)線程,加個(gè)主線程共 8 個(gè),這 8 個(gè)主線程內(nèi)部都只執(zhí)行一個(gè) while(true) ,如下
publicclass Test {
privateint threadCount = 0;
private void dontStop() {
while(true) {
}
}
public void stackLeakByThread() {
while (true) {
// 只創(chuàng)建 7 個(gè)線程, 加上主線程共 8 個(gè)線程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%(大家可以試驗(yàn)一下,這里不貼圖了), 也就是說 8 個(gè) while(true)把 8 個(gè)核全部打滿了,平均一個(gè) while(true)打滿一個(gè)核 ,那么問題來了, 單個(gè)線程執(zhí)行 while(true) 為啥會打滿一個(gè)核呢,CPU 不是按時(shí)間片來分配各個(gè)進(jìn)程的嗎
如圖示:操作系統(tǒng)按時(shí)間片的調(diào)度算法來給不同的進(jìn)程分配 CPU 時(shí)間,如果某個(gè)進(jìn)程時(shí)間片用完了,會讓出 CPU 的控制權(quán)給其他的進(jìn)程執(zhí)行
首先,需要指明的是:CPU 確實(shí)是按時(shí)間片來給不同的進(jìn)程分配它的控制權(quán)的
但 CPU 對時(shí)間片的分配策略是動態(tài)的, 具有偏向性的,簡單理解如下:
Java 中的線程執(zhí)行完系統(tǒng)分配的時(shí)間片后確實(shí)是會讓出 CPU 的執(zhí)行權(quán),但別的進(jìn)程會告訴系統(tǒng)自己沒什么事情要做,不需要那么多的時(shí)間,這個(gè)時(shí)候系統(tǒng)就會切換到下一個(gè)進(jìn)程,直到回到這個(gè)死循環(huán)的進(jìn)程上,而 Java 進(jìn)程無論什么時(shí)候都再循環(huán),都會一直會報(bào)告有事情要做,系統(tǒng)就會把盡可能多的時(shí)間分給它(正所謂會哭的小孩有奶吃),系統(tǒng)會不斷調(diào)高 while(true) 線程的優(yōu)先級,提升它的 CPU 占用時(shí)間片,也就是說 while(true) 這個(gè)死循環(huán)用光了別的進(jìn)程省下的時(shí)間,不讓 CPU 有片刻休息的時(shí)間,導(dǎo)致 CPU 負(fù)載過高,這就像馬太效應(yīng),勤奮的線程執(zhí)行的越努力,其他懶惰的線程就越會被縮短時(shí)間片,越得不到機(jī)會!
畫外音: Windows 系統(tǒng)中就存在一個(gè)稱為「優(yōu)先級推進(jìn)器」(Priority Boosting,可以關(guān)閉)的功能,大致作用就是當(dāng)系統(tǒng)發(fā)現(xiàn)一個(gè)線程執(zhí)行得特別勤奮努力的話,可能會越過線程優(yōu)先級優(yōu)先為此線程分配執(zhí)行時(shí)間
上文提到,發(fā)生 OOM 后, 由于已經(jīng)觀察到 OOM 的現(xiàn)象,所以想把 Java 進(jìn)程通過 Ctrl+C 殺死,但發(fā)現(xiàn)不起作用,如圖示
為啥 Ctrl + C 這種通用的 kill 掉進(jìn)程的方式不起作用呢,我在 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 中的信號處理器確實(shí)收到了終端發(fā)出的 Ctrl + C 的終止信號,但當(dāng)它調(diào)用 Java 進(jìn)程想中止時(shí)發(fā)生了 OOM 導(dǎo)致中斷失敗, 那為啥調(diào)用會發(fā)生 OOM 呢,我猜是因?yàn)樾盘柼幚砥饕獑右粋€(gè)線程來做這種終止通知的操作,而我們知道,當(dāng)前已經(jīng)無法再創(chuàng)建線程了(已經(jīng)發(fā)生 unable to create new native thread 的錯(cuò)誤了)
最后一個(gè)問題,主線程發(fā)生 OOM 后 Java 進(jìn)程居然沒終止,這個(gè)該怎么解釋
Main 主線程與其他的子線程并不是父子關(guān)系,而是平等的關(guān)系,所以主線程雖然因?yàn)?OOM 掛了,但其他子線程并不會停止運(yùn)行,由于子線程們執(zhí)行的 while(true),所以子線程會一直存在,既然它們一直存在,那對應(yīng)的 Java 進(jìn)程就會一直運(yùn)行著。
那怎么讓主線程終止運(yùn)行后,其他線程也可立即結(jié)束呢,可以把這些子線程設(shè)置為守護(hù)線程,創(chuàng)建好 Thread thread 后,可以用 thread.setDaemon(true) 將其設(shè)置成守護(hù)線程,這樣當(dāng)主線程掛了,守護(hù)線程也會立即停止運(yùn)行,原因嘛,也很簡單,既然是守護(hù)線程,那被守護(hù)的線程都掛了,那守護(hù)線程也沒存在的意義了
通過一個(gè) OOM 試驗(yàn)引出了三個(gè)值得思考的問題,相信大家應(yīng)該學(xué)了不少知識點(diǎn),這里還是要提醒一下大家,看到書中的 demo 時(shí),最好能親自去嘗試一下,說不定你能有新的發(fā)現(xiàn)!紙上得來終覺淺,絕知此事要躬行!看完上述內(nèi)容,你們對Java中OOM試驗(yàn)造成的電腦雪崩引發(fā)的示例分析有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。