如何在热点崩溃时强制终止Java进程

5
有没有一种方法确保整个Java进程在Hotspot崩溃时会退出?
我们正在远程的Windows Java进程(java.exe)中托管本地库,以实现进程隔离。但是,我们发现即使有Hotspot崩溃,尽管主线程因热点崩溃而“死亡”,但进程本身并不死亡。我们必须在任务管理器中结束它。
我们认为这可能是因为本地库本身创建了它自己的线程,这些线程使进程保持活动状态。
如果发生热点崩溃,我们希望整个Java进程都能够终止。
我们目前的解决方法是有另一个线程读取生成进程的输出(我们必须读取控制台输出以防止进程阻塞)。我们修改了它来明确查找VM崩溃:
private static String HOTSPOT_CRASH = "# A fatal error has been detected by the Java Runtime Environment";

public void run() {
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        do {
            line = reader.readLine();
            if (line != null) {
                logger.info(prefix + ": " + line);
            }
            if(line.contains(HOTSPOT_CRASH)) {
                logger.error(String.format("FATAL Hotspot crash detected. Killing child process '%s'...", hostProcess));
                hostProcess.destroy();
            }
        } while (line != null);
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

但这只是一个hack:如果JVM足够智能,可以记录下崩溃的信息,那么更好的方法是让它终止当前进程。

有什么方法可以做到这一点吗?理想情况下,这应该是JVM的命令行选项。


3
一个进程不可能只崩溃一部分,您几乎可以确定那里有两个进程。 - Ingo Kegel
有趣的问题!崩溃就是崩溃,所以如果发生了,你无能为力。我可能会建立一个心跳监测器(子进程向父进程发送ping消息,父进程每隔几分钟发送pong)。在没有心跳的情况下退出。 - aishwarya
@Ingo - 不幸的是,我也这样认为。但事实证明,我们遇到了热点崩溃,但进程仍然挂起。只有在任务管理器中杀死它后,它才真正消失。 - Paul Hollingsworth
@aishwarya -> 嗯,JVM仍然有足够的控制权将崩溃信息记录到标准输出 - 如果它能终止当前进程就更好了。 - Paul Hollingsworth
定期从另一个进程测试您的进程是否“存活”,如果没有,就杀掉它。可以只是一个简单的脚本,包含无限循环、睡眠、wget/ping等命令,以及jps和kill命令。 - maximdim
1个回答

0

通常情况下,当检测到致命错误时,虚拟机将退出,但您可以在代码中防止这种情况发生。以下是一些提示:

  • 检查父进程。在Unix上,子进程只有在父进程处理SIGCHLD信号(告诉父进程子进程已终止;这允许父进程读取退出代码,例如)时才能死亡。

    确保您处理输出并且在子进程死亡后至少调用一次waitFor()。这应该清理子进程。

  • 查找非守护线程。如果您的Java应用程序中有任何非守护线程,则可能会阻止虚拟机退出。

    但是,虚拟机错误将终止所有线程,因此我猜测您从未清理过子进程。


我们正在通过TCP套接字与子进程通信。如果子进程遇到热点错误,我们希望在父进程中从套接字读取的线程能够收到“连接被对等方重置”的消息。一旦收到该消息,我们就可以清理子进程。 - Paul Hollingsworth
目前的问题是父进程无法知道子进程已经崩溃:套接字的“读取”调用会一直等待,直到我们在任务管理器中终止该进程。 - Paul Hollingsworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接