从JDK源码看System.exit

前言

在编写的Java程序中有时会遇到用 System.exit 来关闭JVM,其中调用 exit 方法时会包含一个状态参数n,即System.exit(n)。这实际上是一个约定值,若是为0则表示正常关闭,而非0则表示非正常关闭。这里咱们从JDK源码看下不一样状态都是怎么处理的。html

System与Runtime

先看System类的exit方法以下,能够看到它是间接调用了Runtime对象的exit方法。java

public static void exit(int status) {
    Runtime.getRuntime().exit(status);
}复制代码

而Runtime的exit方法以下,先使用SecurityManager检查是否有关闭JVM的权限,容许执行则调用Shutdown的exit方法。bash

public void exit(int status) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkExit(status);
    }
    Shutdown.exit(status);
}复制代码

Shutdown

进入到Shutdown类的exit方法,Java层面还有本身的状态state,它可能值为RUNNING、HOOKS和FINALIZERS,能够看到里面的主要逻辑是:并发

  1. 无论什么状态下,status为非0时不执行任何Finalizer。
  2. 在RUNNING状态下,状态转成HOOKS,而后先执行sequence方法,再执行halt方法中止JVM。
  3. 在FINALIZERS状态下,status为非0时直接 就调用halt方法中止JVM了,而status为0时还须要执行全部的finalizer,以后才调用halt方法中止JVM。
    static void exit(int status) {
         boolean runMoreFinalizers = false;
         synchronized (lock) {
             if (status != 0) runFinalizersOnExit = false;
             switch (state) {
             case RUNNING:       
                 state = HOOKS;
                 break;
             case HOOKS:         
                 break;
             case FINALIZERS:
                 if (status != 0) {
                     halt(status);
                 } else {
                     runMoreFinalizers = runFinalizersOnExit;
                 }
                 break;
             }
         }
         if (runMoreFinalizers) {
             runAllFinalizers();
             halt(status);
         }
         synchronized (Shutdown.class) {
             sequence();
             halt(status);
         }
     }复制代码
    sequence方法主要是控制钩子和Finalizer执行的顺序,判断状态不为HOOKS则直接返回,而后执行全部的钩子,把state改成FINALIZERS,最后执行全部finalizer。
    private static void sequence() {
         synchronized (lock) {
             if (state != HOOKS) return;
         }
         runHooks();
         boolean rfoe;
         synchronized (lock) {
             state = FINALIZERS;
             rfoe = runFinalizersOnExit;
         }
         if (rfoe) runAllFinalizers();
     }复制代码

halt方法

执行JVM是经过halt方法实现,这时System.exit(n)的状态n继续往下传递,最终是调用了一个本地的halt0方法。jvm

static void halt(int status) {
    synchronized (haltLock) {
        halt0(status);
    }
}

static native void halt0(int status);复制代码

对应的本地方法以下,主要是调用了JVM_Halt函数,函数

JNIEXPORT void JNICALL
Java_java_lang_Shutdown_halt0(JNIEnv *env, jclass ignored, jint code)
{
    JVM_Halt(code);
}复制代码

继续往下,JVM_Halt函数主要包含了before_exit函数和vm_exit函数,before_exit函数主要作退出前的一些工做,它只会被执行一次,在多个线程状况下只有获取锁的才能执行,其余线程都必须等。优化

JVM_ENTRY_NO_ENV(void, JVM_Halt(jint code))
  before_exit(thread);
  vm_exit(code);
JVM_END复制代码

而vm_exit函数以下,这里code仍然是Java调用System.exit(n)时传递来的,最主要的是vm_direct_exit函数,它先向jvm发出关闭通知,而后再调用exit函数退出,状态值继续往下传,这时的状态值已经传递到操做系统的API。ui

void vm_exit(int code) {
  Thread* thread = ThreadLocalStorage::is_initialized() ?
    ThreadLocalStorage::get_thread_slow() : NULL;
  if (thread == NULL) {
    vm_direct_exit(code);
  }

  if (VMThread::vm_thread() != NULL) {
    VM_Exit op(code);
    if (thread->is_Java_thread())
      ((JavaThread*)thread)->set_thread_state(_thread_in_vm);
    VMThread::execute(&op); VM Thread.
    vm_direct_exit(code);
  } else {
    vm_direct_exit(code);
  }
  ShouldNotReachHere();
}复制代码
void vm_direct_exit(int code) {
  notify_vm_shutdown();
  os::wait_for_keypress_at_exit();
  ::exit(code);
}复制代码

总结

Java的System.exit(n)的状态码最终是传递到操做系统的API,因此它的含义与操做系统API的含义相关,固然这个过程Java还会有本身的一些机制工做须要处理。能够说目前大多数平台均可以在 main 函数中直接 return退出程序,但某些平台下不能这样处理,因此为了兼容须要使用 exit() 来退出。spa

如下是广告相关阅读操作系统

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有须要的朋友能够到 item.jd.com/12185360.ht… 进行预约。感谢各位朋友。

为何写《Tomcat内核设计剖析》

=========================

相关阅读:

从JDK源码角度看Object
从JDK源码角度看Long
从JDK源码角度看Float
从JDK源码角度看Integer
volatile足以保证数据同步吗
谈谈Java基础数据类型
从JDK源码角度看并发锁的优化
从JDK源码角度看线程的阻塞和唤醒
从JDK源码角度看并发竞争的超时
从JDK源码角度看java并发线程的中断
从JDK源码角度看Java并发的公平性
从JDK源码角度看java并发的原子性如何保证
从JDK源码角度看Byte
从JDK源码角度看Boolean
从JDK源码角度看Short

关注打赏:

这里写图片描述
这里写图片描述

这里写图片描述
这里写图片描述
相关文章
相关标签/搜索