JVM对于signal的处理及案例分析

1.Signal的分类

Signal我理解为进程与进程之间的通讯信号, 或者同一个进程内的处理信号, 这个信号的作用比较单一, 一般是为了关闭进程或者是触发某一个函数(如异常捕获).

1.1 Windows和Linux的Signal区别

Windows的Signal相对少一些, 如下:

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命令发起, 比如kill -1 [pid]是对某一个进程发出SIGHUP信息.

1.2 JVM会处理的Signal

不是每个Signal在JVM中都会被处理, 只有部分的Signal会被处理.

JVM 所使用的信号:

信号的类型为异常、错误、中断和控制

表 1

  • 异常

    无论何时出现无法恢复的情况,操作系统都会同步发出一个相应的异常信号。

  • 错误

    如果 JVM 检测到不能从中恢复的情形,它会发出 SIGABRT。

  • 中断

    将从 JVM 进程外部异步发出中断信号以请求关闭。

  • 控制

    以JVM 为控制目的而使用的其他信号。

信号名称 信号类型 描述 是否被 -Xrs 禁用 是否被 -Xrs:sync 禁用
SIGBUS (7) 异常 访问内存不正确(数据未对准)
SIGSEGV (11) 异常 访问内存不正确(写到不可访问的内存)
SIGILL (4) 异常 非法指令(尝试调用未知的机器指令)
SIGFPE (8) 异常 浮点异常(除数为零)
SIGABRT (6) 错误 异常终止。无论何时检测到 JVM 错误,JVM 都发出该信号。
SIGINT (2) 中断 交互式注意信号(CTRL-C)。JVM 正常退出。
SIGTERM (15) 中断 终止请求。JVM 将正常退出。
SIGHUP (1) 中断 挂起。JVM 正常退出。
SIGQUIT (3) 控制 终端的退出信号。缺省情况下,这将触发 Javadump。
SIGTRAP(5) 控制 由 JIT 使用。
SIGRTMIN (32) 控制 由 JVM 用于内部控制目的。
SIGRTMAX (64) 控制 由 SDK 使用。
SIGCHLD (17) 控制 由 SDK 用于内部控制。

注:

信号名称后提供的数字是该信号的标准数值。

使用 -Xrs(减少信号使用)选项来防止 JVM 处理大多数的信号。有关更多信息,请参阅 Oracle 的 Java™ 应用程序启动程序页面

JVM 线程上的信号 1(SIGHUP)、2(SIGINT)、4(SIGILL)、7(SIGBUS)、8(SIGFPE)、11(SIGSEGV)和 15(SIGTERM)导致 JVM 关闭;因此,应用程序信号处理程序不应该尝试从这些信号恢复,除非它不再需要 JVM。

以上表格引用原文链接:

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


至于JVM是如何处理这些Signal的, 请参考以下链接:

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默认处理Signal的行为, 我们还可以自定义SignalHandler来做一些额外的工作, 比如在关闭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关闭与关闭钩子

前面说到, Signal的一大作用是关闭进程, 然而Java提供了Shutdown Hook(关闭钩子)机制,它让我们在程序正常退出或者发生异常时能有机会做一些清场工作。

关闭钩子使用的方法也很简单,Runtime.getRuntime().addShutdownHook(Thread hook)即可。关闭钩子其实可以看成是一个已经初始化了的但还没启动的线程,当JVM关闭时会并发地执行注册的所有关闭钩子

JVM的关闭方式可以分为三种:

  1. 正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等)
  2. 强制关闭:通过调用Runtime.halt方法或者是在操作系统中直接kill(发送SIGKILL信号)掉JVM进程
  3. 异常关闭:运行中遇到RuntimeException异常, OOM错误等。

注意: Hook线程在JVM 正常关闭才会执行,在强制关闭时不会执行。(异常关闭没试过, 有空试一下..)

另外在使用关闭钩子还要注意以下几点:

  1. 不能在钩子调用System.exit(),否则卡住JVM的关闭过程,但是可以调用Runtime.halt()
  2. 不能再钩子中再进行钩子的添加和删掉操作,否则将会抛出IllegalStateException。
  3. System.exit()之后添加的钩子无效。
  4. 当JVM收到SIGTERM命令(比如操作系统在关闭时)后,如果钩子线程在一定时间没有完成,那么Hook线程可能在执行过程中被终止。
  5. Hool线程中同样会抛出异常,如果抛出异常又不处理,那么钩子的执行序列就会被停止。

Spring在初始化容器的时候就会注册一个hook线程用于清理容器.

[图片上传失败...(image-3b9611-1513089974690)]

3.客服后台系统JVM崩溃的案例

3.1 现象

JVM进程已经不在了, 重启后, 几分钟到半小时之间, 会看到获取不到spring bean的错误日志, 同时系统服务异常.

[图片上传失败...(image-cfc2ef-1513089974690)]

奇怪的是, 2台集群中的其他一台一直都是稳定运行, 只有这台是一直异常状态.

3.2 现场分析

从以上的日志, 可以看出spring容器已经在销毁中了, 感觉是一个正常的关闭系统的流程.

在监控系统(Marvin)中观察了内存的情况, 没有什么波动, 基本排除了oom的情况.

接下来, 我使用jstack输出了当时的线程栈信息, 保留现场痕迹.

[图片上传失败...(image-508b17-1513089974690)]

由上图所示, 从jstack日志中发现了2个关键的点:

  1. spring确实在执行销毁容器的操作
  2. 有一个SIGHUP handler的线程, 在执行

自此, 大家可能已经看出来, SIGHUP正是JVM会处理的Signal之一, 并且在上面的表格中已经清楚的写着SIGHUP的操作是挂起, 让JVM正常退出, SIGHUP中断类型的信号, 上面对于中断类型的信号是这样描述的: 将从 JVM 进程外部异步发出中断信号以请求关闭。

结论:

基本确定是由外部发出的中断信号, 导致JVM正常退出. 那么如果能找到信号来源的话, 这个事情就清楚了.

可惜的是, 找了很多资料, 始终没有找到确定信号来源的方案. Linux本身也没有相关的日志, JVM也只能获取信号的名称, 对于信号源也是无法确定.(如果有这方面研究的同学望告知..)

3.3 解决方案

找不到根本原因, 那么只能是想办法绕过这个问题.

所幸的是, 在搜索问题的时候, 让我知道了Linux还有一个nohup的命令.

nohup命令可以将程序以忽略挂起信号(SIGHUP)的方式运行起来,被运行的程序的输出信息将不会显示到终端。

于是把JVM的启动脚本改动了一下:

[图片上传失败...(image-7897ca-1513089974690)]

再次启动后, 稳定运行, 问题解决.

实际上通过JVM本身-Xrs的参数应该也能控制忽略SIGHUP信号的, 但是时间关系, 我没去实验..

3.4 总结

这里案例也让我学到了很多JVM的处理细节.

同时也有了一些思考:

排查线上故障的基本步骤无非就是

  1. 看现象, 初步判断故障的原因或范围(如果能直接确定问题当然是最好)
  2. 看异常日志, 判断异常是否发生以及发生的代码位置, 从而确定具体的原因
  3. 结合监控(Marvin等), 观察机器的运行情况, 辅助排查问题
  4. 第一时间收集现场证据(如jstack, jmap, gc.log等), 以便后面的问题分析

实际上, 前面3步基本上已经能解决大部分的问题了, 剩下的一些疑难问题才会用到第4步. 但是第4步的操作对于实时性的要求是最高的, 必须第一时间搞定, 晚一点可能你就捕捉不到有效的证据了.

在出现故障的情况下, 有时候难免手忙脚乱, 如果有一个可以自动化收集现场证据的脚本, 在出现这种疑难问题的时候将会是一个极大的助力.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容