JVM源码剖析之为什么ctrl+c能够结束JVM进程

2023-12-13 15:32:47

版本信息:

jdk版本:jdk8u40

写在前面:

在Linux和mac操作系统上当开发者运行Java程序(当然不限于Java程序)想停止结束进程时一般都使用ctrl+c或者kill -9 进程号来结束。那么肯定会有开发者对ctrl+c为什么能结束JVM进程感到兴趣,所以笔者写在了这篇关于ctrl+c结束JVM进程的文章,带领各位读者研究为什么ctrl+c能结束,以及看了源码后得到的一些 " 有趣?" 的事情~

源码论证:

在看源码之前,有一个小案例给读者带来一个" 有趣?"的事情

public class SignalTest {

    public static void main(String[] args) throws InterruptedException {
        Signal.handle(new Signal("INT"), sn ->{
            // 哈哈哈 这样在Linux和mac上只能用kill -9关闭
            // ctrl+c关闭不了,甚至还会被嘲笑~
            System.out.println("关闭不了的,不要试啦");
        });
        Thread.sleep(1000000);
    }

}

此案例可以直接运行在Linux或者mac上(可以在控制台中运行,也可以在eclipse或者IDEA上运行,如果在eclipse或者IDEA可以直接点击结束按钮也是一样,控制台运行的话直接ctrl+c),你会发现根本关闭不了,甚至还会被嘲笑啊~? 如果要关闭,只能用kill -9 进程号关闭此JVM进程。

如果有C/C++ 基础的读者应该明白,不管是ctrl+c (SIGINT) 或者 kill -9 (SIGKILL) 都是基于操作系统的信号机制来实现(这里不重点赘述操作系统的信号机制),但是有一点需要说明白的是:信号区分强制信号和可自定义信号等等,ctrl+c (SIGINT)就是自定义信号,进程可以通过系统调用去更改信号处理,而kill -9 (SIGKILL)就是强制信号,使用这个信号的话操作系统会直接杀死某个进程(且无法被自定义修改)。所以接下来我们需要去弄明白JVM是如何对ctrl+c (SIGINT) 自定义处理的。

在Java类库中的System.java类中存在initializeSystemClass方法,此方法你会发现没有人调用它,没错,他是JVM启动的时候,JVM调用的方法。src/share/vm/runtime/thread.cpp 文件中

static void call_initializeSystemClass(TRAPS) {
  // 获取到System的类
  Klass* k =  SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(), true, CHECK);
  instanceKlassHandle klass (THREAD, k);
  // 调用System的initializeSystemClass方法
  JavaValue result(T_VOID);
  JavaCalls::call_static(&result, klass, vmSymbols::initializeSystemClass_name(),
                                         vmSymbols::void_method_signature(), CHECK);
}

这里是在JVM启动的时候调用的方法,此方法中会去调用System类中的initializeSystemClass方法

private static void initializeSystemClass() {

    ………… // 省略无关代码

    // 设置关闭的信号
    Terminator.setup();

    ………… // 省略无关代码
    
}

?这里省略掉无关代码,可以看到在初始化的过程中调用了Terminator.setup()方法,此方法会初始化信号相关的代码~

static void setup() {
    if (handler != null) 
        return;
    // 信号处理
    SignalHandler sh = new SignalHandler() {
        public void handle(Signal sig) {
            // 进行JVM的关闭的HOOK回调
            // 然后关闭JVM
            Shutdown.exit(sig.getNumber() + 0200);
        }
    };
    handler = sh;
    try {
        // 完成HUP信号和信号处理的映射
        Signal.handle(new Signal("HUP"), sh);
    } catch (IllegalArgumentException e) {
    }
    try {
        // 完成INT(ctrl+c)信号和信号处理的映射
        Signal.handle(new Signal("INT"), sh);
    } catch (IllegalArgumentException e) {
    }
    try {
        // 完成TERM信号和信号处理的映射
        Signal.handle(new Signal("TERM"), sh);
    } catch (IllegalArgumentException e) {
    }
}

关于Java的信号处理机制本篇文章不会展开讲解,上文中对 HUP 、INT(ctrl+c)、TERM信号做了自定义的处理,当其他进程往JVM发送这三个信号时,JVM会回调上文的SignalHandler处理函数,而上文SignalHandler处理函数会优雅关闭掉JVM(为什么说时优雅呢,因为关闭前会做处理,比如执行各种关闭前的回调函数等等)

我们看到这里就明白了,为什么SIGINT(ctrl+c)能关闭掉JVM。此时,我们见识到了Java的信号处理机制,那么我们把默认的SIGINT(ctrl+c)处理函数换了不就关闭不了了,比如本文开始的案例。

总结:

本文讲解了JVM使用信号机制优雅的关闭JVM,也讲解了如何使用Java信号机制把JVM默认的信号处理函数给替换导致JVM无法正常关闭~

文章来源:https://blog.csdn.net/qq_43799161/article/details/134966632
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。