是什么导致Java在System.exit()之后继续运行?

uck*_*man 20 java exit processbuilder

我有一个Java程序,它是ProcessBuilder从另一个Java程序启动的. System.exit(0)从子程序调用,但对于我们的一些用户(在Windows上),java.exe与子项关联的进程不会终止.子程序没有关闭挂钩,也没有SecurityManager可能停止System.exit()终止VM的挂钩.我无法在Linux或Windows Vista上自行重现此问题.到目前为止,问题的唯一报告来自两个Windows XP用户和一个Vista用户,使用两个不同的JRE(1.6.0_15和1.6.0_18),但他们每次都能够重现问题.

任何人都可以提出为什么JVM无法在之后终止System.exit(),然后只在某些机器上终止的原因?

编辑1:我让用户安装JDK,这样我们就可以从违规的虚拟机中获取一个线程转储.用户告诉我的是,一旦他点击我的菜单中的"退出"项目,VM进程就会从VisualVM中消失---但是,根据Windows任务管理器,该进程尚未终止,无论多长时间用户等待(分钟,小时),它永远不会终止.

编辑2:我现在已经确认Process.waitFor()在父程序中永远不会返回至少一个有问题的用户.总而言之:孩子VM似乎已经死了(VisualVM甚至没有看到它),但是父母仍然认为这个过程是实时的,Windows也是如此.

Mol*_*Ice 9

如果您的代码(或您使用的库)具有关闭钩子或终结器未完全完成,则会发生这种情况.

一个更有活力(所以应该只在极端情况下使用!)强制关机的方法是运行:

Runtime.getRuntime().halt(0);
Run Code Online (Sandbox Code Playgroud)


小智 6

父进程有一个专用于使用子进程STDOUT和STDERR(将该输出传递给日志文件)的线程.据我所知,这些工作正常,因为我们看到了我们期望在日志中看到的所有输出

当我使用stdout/stderr时,我的程序没有从任务mgr中消失,我遇到了类似的问题.在我的情况下,如果我在调用system.exit()之前关闭了正在侦听的流,那么javaw.exe就会挂起.奇怪的是,它没有写入流...

在我的情况下,解决方案是简单地刷新流而不是在存在之前关闭它.当然,你可以随时刷新然后在退出之前重定向回stdout和stderr.


Jav*_*cal 5

检查是否存在死锁。

例如,

Runtime.getRuntime().addShutdownHook(new Thread(this::close));
System.exit(1);
Run Code Online (Sandbox Code Playgroud)

并在close()

public void close() {
// some code
System.exit(1);
}
Run Code Online (Sandbox Code Playgroud)

实际上System.exit()调用关闭钩子和终结器。因此,如果您的 shutdown 钩子出于某种原因在内部再次调用 exit() (例如,当引发close()您想要退出程序的异常时,那么您也会exit()在那里调用)。像这样..

public void close() {
  try {
  // some code that raises exception which requires to exit the program
  } catch(Exception exceptionWhichWhenOccurredShouldExitProgram) {
    log.error(exceptionWhichWhenOccurredShouldExitProgram);
    System.exit(1);
  }
}
Run Code Online (Sandbox Code Playgroud)

虽然抛出异常是一个很好的做法,但有些人可能会选择记录并退出。

另请注意,Ctrl+C如果存在死锁,这也将不起作用。因为它还调用关闭挂钩。

无论如何,如果是这种情况,可以通过以下解决方法解决问题:

private static AtomicBoolean exitCalled=new AtomicBoolean();

    private static void exit(int status) {
        if(!exitCalled.get()) {
            exitCalled.set(true);
            System.exit(status);
        }
    }

    Runtime.getRuntime().addShutdownHook(new Thread(MyClass::close));
      exit(1);
    }

    private static void close() {
        exit(1);
    }
Run Code Online (Sandbox Code Playgroud)

PS:我觉得上面的exit()版本实际上必须只写在System.exit()方法中(可能是 JDK 的一些 PR?)因为,实际上没有任何意义(至少从我看来)System.exit()