JVM對(duì)于signal的處理及案例分析

1.Signal的分類(lèi)

Signal我理解為進(jìn)程與進(jìn)程之間的通訊信號(hào), 或者同一個(gè)進(jìn)程內(nèi)的處理信號(hào), 這個(gè)信號(hào)的作用比較單一, 一般是為了關(guān)閉進(jìn)程或者是觸發(fā)某一個(gè)函數(shù)(如異常捕獲).

1.1 Windows和Linux的Signal區(qū)別

Windows的Signal相對(duì)少一些, 如下:

ABRT    SIGABRT
FPE     SIGFPE
SEGV    SIGSEGV
INT     SIGINT
TERM    SIGTERM
ILL     SIGILL

Linux的Signal比較多, 如下:

 HUP    SIGHUP
 INT    SIGINT
 QUIT   SIGQUIT
 ILL    SIGILL
 TRAP   SIGTRAP
 ABRT   SIGABRT
 IOT    SIGIOT (*)
 EMT    SIGEMT (*)
 FPE    SIGFPE
 KILL   SIGKILL
 BUS    SIGBUS
 SEGV   SIGSEGV
 SYS    SIGSYS
 PIPE   SIGPIPE
 ALRM   SIGALRM
 TERM   SIGTERM
 STKFLT SIGSTKFLT
 USR1    SIGUSR1
 USR2    SIGUSR2
 CHLD    SIGCHLD
 PWR    SIGPWR
 WINCH   SIGWINCH
 URG    SIGURG
 POLL    SIGPOLL
 IO     SIGIO
 STOP    SIGSTOP
 TSTP    SIGTSTP
 CONT    SIGCONT
 TTIN    SIGTTIN
 TTOU    SIGTTOU
 VTALRM  SIGVTALRM
 PROF    SIGPROF
 XCPU    SIGXCPU
 XFSZ    SIGXFSZ
 UNUSED  SIGUNUSED
 SWI    SIGSWI

Linux中的Signal可以由kill命令發(fā)起, 比如kill -1 [pid]是對(duì)某一個(gè)進(jìn)程發(fā)出SIGHUP信息.

1.2 JVM會(huì)處理的Signal

不是每個(gè)Signal在JVM中都會(huì)被處理, 只有部分的Signal會(huì)被處理.

JVM 所使用的信號(hào):

信號(hào)的類(lèi)型為異常厨姚、錯(cuò)誤、中斷和控制

表 1

  • 異常

    無(wú)論何時(shí)出現(xiàn)無(wú)法恢復(fù)的情況瘤运,操作系統(tǒng)都會(huì)同步發(fā)出一個(gè)相應(yīng)的異常信號(hào)读规。

  • 錯(cuò)誤

    如果 JVM 檢測(cè)到不能從中恢復(fù)的情形宜咒,它會(huì)發(fā)出 SIGABRT幌绍。

  • 中斷

    將從 JVM 進(jìn)程外部異步發(fā)出中斷信號(hào)以請(qǐng)求關(guān)閉修械。

  • 控制

    以JVM 為控制目的而使用的其他信號(hào)趾牧。

信號(hào)名稱(chēng) 信號(hào)類(lèi)型 描述 是否被 -Xrs 禁用 是否被 -Xrs:sync 禁用
SIGBUS (7) 異常 訪問(wèn)內(nèi)存不正確(數(shù)據(jù)未對(duì)準(zhǔn))
SIGSEGV (11) 異常 訪問(wèn)內(nèi)存不正確(寫(xiě)到不可訪問(wèn)的內(nèi)存)
SIGILL (4) 異常 非法指令(嘗試調(diào)用未知的機(jī)器指令)
SIGFPE (8) 異常 浮點(diǎn)異常(除數(shù)為零)
SIGABRT (6) 錯(cuò)誤 異常終止。無(wú)論何時(shí)檢測(cè)到 JVM 錯(cuò)誤肯污,JVM 都發(fā)出該信號(hào)翘单。
SIGINT (2) 中斷 交互式注意信號(hào)(CTRL-C)。JVM 正常退出蹦渣。
SIGTERM (15) 中斷 終止請(qǐng)求哄芜。JVM 將正常退出。
SIGHUP (1) 中斷 掛起柬唯。JVM 正常退出认臊。
SIGQUIT (3) 控制 終端的退出信號(hào)。缺省情況下锄奢,這將觸發(fā) Javadump失晴。
SIGTRAP(5) 控制 由 JIT 使用剧腻。
SIGRTMIN (32) 控制 由 JVM 用于內(nèi)部控制目的。
SIGRTMAX (64) 控制 由 SDK 使用涂屁。
SIGCHLD (17) 控制 由 SDK 用于內(nèi)部控制书在。

注:

信號(hào)名稱(chēng)后提供的數(shù)字是該信號(hào)的標(biāo)準(zhǔn)數(shù)值。

使用 -Xrs(減少信號(hào)使用)選項(xiàng)來(lái)防止 JVM 處理大多數(shù)的信號(hào)拆又。有關(guān)更多信息儒旬,請(qǐng)參閱 Oracle 的 Java? 應(yīng)用程序啟動(dòng)程序頁(yè)面

JVM 線程上的信號(hào) 1(SIGHUP)帖族、2(SIGINT)栈源、4(SIGILL)、7(SIGBUS)盟萨、8(SIGFPE)、11(SIGSEGV)和 15(SIGTERM)導(dǎo)致 JVM 關(guān)閉了讨;因此捻激,應(yīng)用程序信號(hào)處理程序不應(yīng)該嘗試從這些信號(hào)恢復(fù),除非它不再需要 JVM前计。

以上表格引用原文鏈接:

https://www.ibm.com/support/knowledgecenter/zh/SSYKE2_7.0.0/com.ibm.java.lnx.70.doc/user/sighand.html


至于JVM是如何處理這些Signal的, 請(qǐng)參考以下鏈接:

https://www.ibm.com/support/knowledgecenter/zh/SSYKE2_7.0.0/com.ibm.java.lnx.70.doc/user/signals.html

1.3 JVM中的自定義SignalHandler

除了JVM默認(rèn)處理Signal的行為, 我們還可以自定義SignalHandler來(lái)做一些額外的工作, 比如在關(guān)閉JVM之前做一些回收或記錄的事情.

例子:

import sun.misc.Signal;
import sun.misc.SignalHandler;
import java.lang.reflect.*;
  
public class Main
{
   public static void main(String []args) {
      DebugSignalHandler.listenTo("HUP");
      DebugSignalHandler.listenTo("INT");
      DebugSignalHandler.listenTo("KILL");
      DebugSignalHandler.listenTo("TERM");
  
      while (true) {
         try {
            Thread.sleep(1000);
         }
         catch(InterruptedException e) {
         }
      }
   }
}
  
class DebugSignalHandler implements SignalHandler
{
   public static void listenTo(String name) {
      Signal signal = new Signal(name);
      Signal.handle(signal, new DebugSignalHandler());
   }
 
   public void handle(Signal signal) {
      System.out.println("Signal: " + signal);
      if (signal.toString().trim().equals("SIGTERM")) {
         System.out.println("SIGTERM raised, terminating...");
         System.exit(1);
      }
   }
}

2.深入JVM關(guān)閉與關(guān)閉鉤子

前面說(shuō)到, Signal的一大作用是關(guān)閉進(jìn)程, 然而Java提供了Shutdown Hook(關(guān)閉鉤子)機(jī)制胞谭,它讓我們?cè)诔绦蛘M顺龌蛘甙l(fā)生異常時(shí)能有機(jī)會(huì)做一些清場(chǎng)工作。

關(guān)閉鉤子使用的方法也很簡(jiǎn)單男杈,Runtime.getRuntime().addShutdownHook(Thread hook)即可丈屹。關(guān)閉鉤子其實(shí)可以看成是一個(gè)已經(jīng)初始化了的但還沒(méi)啟動(dòng)的線程,當(dāng)JVM關(guān)閉時(shí)會(huì)并發(fā)地執(zhí)行注冊(cè)的所有關(guān)閉鉤子伶棒。

JVM的關(guān)閉方式可以分為三種:

  1. 正常關(guān)閉:當(dāng)最后一個(gè)非守護(hù)線程結(jié)束或者調(diào)用了System.exit或者通過(guò)其他特定平臺(tái)的方法關(guān)閉(發(fā)送SIGINT旺垒,SIGTERM信號(hào)等)
  2. 強(qiáng)制關(guān)閉:通過(guò)調(diào)用Runtime.halt方法或者是在操作系統(tǒng)中直接kill(發(fā)送SIGKILL信號(hào))掉JVM進(jìn)程
  3. 異常關(guān)閉:運(yùn)行中遇到RuntimeException異常, OOM錯(cuò)誤等。

注意: Hook線程在JVM 正常關(guān)閉才會(huì)執(zhí)行肤无,在強(qiáng)制關(guān)閉時(shí)不會(huì)執(zhí)行先蒋。(異常關(guān)閉沒(méi)試過(guò), 有空試一下..)

另外在使用關(guān)閉鉤子還要注意以下幾點(diǎn):

  1. 不能在鉤子調(diào)用System.exit(),否則卡住JVM的關(guān)閉過(guò)程宛渐,但是可以調(diào)用Runtime.halt()竞漾。
  2. 不能再鉤子中再進(jìn)行鉤子的添加和刪掉操作,否則將會(huì)拋出IllegalStateException塑煎。
  3. System.exit()之后添加的鉤子無(wú)效底哗。
  4. 當(dāng)JVM收到SIGTERM命令(比如操作系統(tǒng)在關(guān)閉時(shí))后勇边,如果鉤子線程在一定時(shí)間沒(méi)有完成,那么Hook線程可能在執(zhí)行過(guò)程中被終止笔时。
  5. Hool線程中同樣會(huì)拋出異常,如果拋出異常又不處理仗岸,那么鉤子的執(zhí)行序列就會(huì)被停止糊闽。

Spring在初始化容器的時(shí)候就會(huì)注冊(cè)一個(gè)hook線程用于清理容器.

[圖片上傳失敗...(image-3b9611-1513089974690)]

3.客服后臺(tái)系統(tǒng)JVM崩潰的案例

3.1 現(xiàn)象

JVM進(jìn)程已經(jīng)不在了, 重啟后, 幾分鐘到半小時(shí)之間, 會(huì)看到獲取不到spring bean的錯(cuò)誤日志, 同時(shí)系統(tǒng)服務(wù)異常.

[圖片上傳失敗...(image-cfc2ef-1513089974690)]

奇怪的是, 2臺(tái)集群中的其他一臺(tái)一直都是穩(wěn)定運(yùn)行, 只有這臺(tái)是一直異常狀態(tài).

3.2 現(xiàn)場(chǎng)分析

從以上的日志, 可以看出spring容器已經(jīng)在銷(xiāo)毀中了, 感覺(jué)是一個(gè)正常的關(guān)閉系統(tǒng)的流程.

在監(jiān)控系統(tǒng)(Marvin)中觀察了內(nèi)存的情況, 沒(méi)有什么波動(dòng), 基本排除了oom的情況.

接下來(lái), 我使用jstack輸出了當(dāng)時(shí)的線程棧信息, 保留現(xiàn)場(chǎng)痕跡.

[圖片上傳失敗...(image-508b17-1513089974690)]

由上圖所示, 從jstack日志中發(fā)現(xiàn)了2個(gè)關(guān)鍵的點(diǎn):

  1. spring確實(shí)在執(zhí)行銷(xiāo)毀容器的操作
  2. 有一個(gè)SIGHUP handler的線程, 在執(zhí)行

自此, 大家可能已經(jīng)看出來(lái), SIGHUP正是JVM會(huì)處理的Signal之一, 并且在上面的表格中已經(jīng)清楚的寫(xiě)著SIGHUP的操作是掛起, 讓JVM正常退出, SIGHUP中斷類(lèi)型的信號(hào), 上面對(duì)于中斷類(lèi)型的信號(hào)是這樣描述的: 將從 JVM 進(jìn)程外部異步發(fā)出中斷信號(hào)以請(qǐng)求關(guān)閉梳玫。

結(jié)論:

基本確定是由外部發(fā)出的中斷信號(hào), 導(dǎo)致JVM正常退出. 那么如果能找到信號(hào)來(lái)源的話, 這個(gè)事情就清楚了.

可惜的是, 找了很多資料, 始終沒(méi)有找到確定信號(hào)來(lái)源的方案. Linux本身也沒(méi)有相關(guān)的日志, JVM也只能獲取信號(hào)的名稱(chēng), 對(duì)于信號(hào)源也是無(wú)法確定.(如果有這方面研究的同學(xué)望告知..)

3.3 解決方案

找不到根本原因, 那么只能是想辦法繞過(guò)這個(gè)問(wèn)題.

所幸的是, 在搜索問(wèn)題的時(shí)候, 讓我知道了Linux還有一個(gè)nohup的命令.

nohup命令可以將程序以忽略掛起信號(hào)(SIGHUP)的方式運(yùn)行起來(lái),被運(yùn)行的程序的輸出信息將不會(huì)顯示到終端右犹。

于是把JVM的啟動(dòng)腳本改動(dòng)了一下:

[圖片上傳失敗...(image-7897ca-1513089974690)]

再次啟動(dòng)后, 穩(wěn)定運(yùn)行, 問(wèn)題解決.

實(shí)際上通過(guò)JVM本身-Xrs的參數(shù)應(yīng)該也能控制忽略SIGHUP信號(hào)的, 但是時(shí)間關(guān)系, 我沒(méi)去實(shí)驗(yàn)..

3.4 總結(jié)

這里案例也讓我學(xué)到了很多JVM的處理細(xì)節(jié).

同時(shí)也有了一些思考:

排查線上故障的基本步驟無(wú)非就是

  1. 看現(xiàn)象, 初步判斷故障的原因或范圍(如果能直接確定問(wèn)題當(dāng)然是最好)
  2. 看異常日志, 判斷異常是否發(fā)生以及發(fā)生的代碼位置, 從而確定具體的原因
  3. 結(jié)合監(jiān)控(Marvin等), 觀察機(jī)器的運(yùn)行情況, 輔助排查問(wèn)題
  4. 第一時(shí)間收集現(xiàn)場(chǎng)證據(jù)(如jstack, jmap, gc.log等), 以便后面的問(wèn)題分析

實(shí)際上, 前面3步基本上已經(jīng)能解決大部分的問(wèn)題了, 剩下的一些疑難問(wèn)題才會(huì)用到第4步. 但是第4步的操作對(duì)于實(shí)時(shí)性的要求是最高的, 必須第一時(shí)間搞定, 晚一點(diǎn)可能你就捕捉不到有效的證據(jù)了.

在出現(xiàn)故障的情況下, 有時(shí)候難免手忙腳亂, 如果有一個(gè)可以自動(dòng)化收集現(xiàn)場(chǎng)證據(jù)的腳本, 在出現(xiàn)這種疑難問(wèn)題的時(shí)候?qū)?huì)是一個(gè)極大的助力.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末提澎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子念链,更是在濱河造成了極大的恐慌盼忌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掂墓,死亡現(xiàn)場(chǎng)離奇詭異谦纱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)君编,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)跨嘉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吃嘿,你說(shuō)我怎么就攤上這事祠乃。” “怎么了兑燥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵亮瓷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我降瞳,道長(zhǎng)嘱支,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任挣饥,我火速辦了婚禮除师,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扔枫。我一直安慰自己馍盟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布茧吊。 她就那樣靜靜地躺著贞岭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搓侄。 梳的紋絲不亂的頭發(fā)上瞄桨,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音讶踪,去河邊找鬼芯侥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱查。 我是一名探鬼主播廓俭,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唉工!你這毒婦竟也來(lái)了研乒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淋硝,失蹤者是張志新(化名)和其女友劉穎雹熬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谣膳,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竿报,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了继谚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烈菌。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖花履,靈堂內(nèi)的尸體忽然破棺而出芽世,到底是詐尸還是另有隱情,我是刑警寧澤臭挽,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布捂襟,位于F島的核電站咬腕,受9級(jí)特大地震影響欢峰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涨共,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一纽帖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧举反,春花似錦懊直、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至魁索,卻和暖如春融撞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粗蔚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工尝偎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓致扯,卻偏偏與公主長(zhǎng)得像肤寝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抖僵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容