Java 虚拟机的关机方式

文章转自: Java 虚拟机的关机方式

JVM 是一个虚拟机,既然是虚拟的机器,就必然涉及到关机操作。JVM 关闭时,首先调用关闭钩子,所有钩子执行完毕后,如果需要进行垃圾回收就调用 finalize 方法,否则直接关闭虚拟机。JVM 关闭过程中,不会中断或停止任何线程,在最终关闭虚拟机时强制关闭所有线程。

关闭钩子

关闭钩子是一个可以在 JVM 关闭时执行的回调,可以通过 Runtime.addShutdownHook 进行注册。JVM 关闭时首先会调用这些关闭钩子,但不会保证关闭钩子执行的顺序。关闭钩子的执行时间要尽可能短,不应该再做耗时的操作,因为这会影响 JVM 的关闭时间。

1
Runtime.getRuntime().addShutdownHook(new Thread(){    //TODO});

如果调用关闭钩子时还有线程在运行,那么关闭钩子将和这些线程同时运行。因此,关闭钩子的操作必须保证是线程安全的,访问数据需要使用同步机制,要避免死锁。同时,关闭钩子需要考虑 JVM 关闭的所有可能性,不能假设关闭的原因,也不应该尝试去分析 JVM 关闭的原因。关闭钩子通常用于服务的清理工作,如 dubbo 使用关闭钩子来关闭连接并通知注册中心注销服务。但关闭钩子是并发执行的,需要考虑多个钩子之间的相互影响,如提前关闭日志服务可能导致其他钩子或线程无法再使用日志。建议只使用一个关闭钩子来处理所有的事情,这样可以确保任务串行执行,从而避免多个钩子之间的竞争和死锁。

守护线程

Java 中的线程分为普通线程和守护线程。一个线程被创建时会继承创建它的那个线程的守护状态。JVM 启动时创建的线程除了主线程是普通线程,其他线程(如 GC 等)都是守护线程。当线程退出时,JVM 会检查剩余线程的状态,如果剩余的线程都是守护线程已经没有普通线程,那么 JVM 会进行关闭操作。JVM 最终关闭时,守护线程会被直接抛弃,既不会执行 finally 也不会执行回卷栈。因此应该尽量不要使用守护线程,使用时也应该进行简单的操作。

垃圾回收JVM

关闭的最后一步是进行垃圾回收,主要是文件或套接字资源。这一步主要是调用 finalize 方法进行最后的资源释放。finalize 方法访问的数据可能会被其他线程并发访问,必须对访问进行同步控制。JVM 不保证何时调用 finalize 方法,甚至无法保证是否调用 finalize 方法。因此尽量不要使用 finalize 来释放资源,而应该在 finally 中显式调用 close 方法来关闭资源。

强行关闭

如果 JVM 遇到问题或者无法关闭,我们可以使用 kill 命令或其他虚拟机自己实现的方法进行强行关闭。强行关闭仅仅是关闭 JVM,不会运行关闭钩子。

文章目录
  1. 1. 关闭钩子
  2. 2. 守护线程
  3. 3. 垃圾回收JVM
  4. 4. 强行关闭
|