什么会导致 Java 在 System.exit() 之后继续运行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2614774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What can cause Java to keep running after System.exit()?
提问by uckelman
I have a Java program which is being started via ProcessBuilder
from another Java program.
System.exit(0)
is called from the child program, but for some of our users (on Windows) the java.exe
process associated with the child doesn't terminate. The child program has no shutdown hooks, nor does it have a SecurityManager
which might stop System.exit()
from terminating the VM. I can't reproduce the problem myself on Linux or Windows Vista. So far, the only reports of the problem come from two Windows XP users and one Vista user, using two different JREs (1.6.0_15 and 1.6.0_18), but they're able to reproduce the problem every time.
我有一个 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),但他们每次都能重现该问题。
Can anyone suggest reasons why the JVM would fail to terminate after System.exit()
, and then only on some machines?
谁能提出 JVM 在 之后无法终止的原因System.exit()
,然后只能在某些机器上?
Edit 1:I got the user to install the JDK so we could get a thread dump from the offending VM. What the user told me is that the VM process disappears from VisualVM as soon as he clicks on the 'Quit' item in my menu---but, according to Windows Task Manager, the process hasn't terminated, and no matter how long the user waits (minutes, hours), it never terminates.
编辑 1:我让用户安装 JDK,这样我们就可以从有问题的 VM 中获取线程转储。用户告诉我的是,只要他点击我菜单中的“退出”项,VM 进程就会从 VisualVM 中消失——但是,根据 Windows 任务管理器,该进程并没有终止,无论多久用户等待(分钟,小时),它永远不会终止。
Edit 2:I have confirmed now that Process.waitFor()
in the parent program never returns for at least one of the users having the problem. So, to summarize: The child VM seems to be dead (VisualVM doesn't even see it) but the parent still sees the process as live and so does Windows.
编辑 2:我现在已经确认,Process.waitFor()
在父程序中,至少有一个有问题的用户永远不会返回。所以,总结一下:子虚拟机似乎已经死了(VisualVM 甚至没有看到它),但父虚拟机仍然将进程视为活动的,Windows 也是如此。
采纳答案by Andrew McVeigh
The parent process has one thread dedicated to consuming each of the child's STDOUT and STDERR (which passes that output through to a log file). So far as I can see, those are working properly, since we're seeing all the output we expect to see in the log
父进程有一个线程专用于使用子进程的 STDOUT 和 STDERR(将输出传递到日志文件)。就我所见,这些工作正常,因为我们看到了我们希望在日志中看到的所有输出
i had a similar problem with my program not disappearing from task mgr when i was consuming the stdout/stderr. in my case, if I closed the stream that was listening before calling system.exit() then the javaw.exe hung around. strange, it wasn't writing to the stream...
我有一个类似的问题,当我使用 stdout/stderr 时,我的程序没有从任务管理器中消失。就我而言,如果我在调用 system.exit() 之前关闭了正在侦听的流,那么 javaw.exe 就会挂起。奇怪的是,它没有写入流...
the solution in my case was to simply flush the stream rather than close it before existing. of course, you could always flush and then redirect back to stdout and stderr before exit.
在我的情况下,解决方案是简单地刷新流而不是在存在之前关闭它。当然,您可以随时刷新,然后在退出前重定向回 stdout 和 stderr。
回答by Chris Dolan
Maybe a badly written finalizer? A shutdown hook was my first thought when I read the subject line. Speculation: would a thread that catches InterruptedException and keeps on running anyway hold up the exit process?
也许是一个写得很糟糕的终结器?当我阅读主题行时,我的第一个想法是关闭挂钩。推测:捕获 InterruptedException 并继续运行的线程是否会阻止退出进程?
It seems to me that if the problem is reproducible, you should be able to attach to the JVM and get a thread list/stack trace that shows what is hung up.
在我看来,如果问题是可重现的,您应该能够附加到 JVM 并获得显示挂断内容的线程列表/堆栈跟踪。
Are you sure that the child is still really running and that it's not just an unreaped zombie process?
你确定这个子进程真的还在运行,而且不只是一个未收割的僵尸进程?
回答by Michael Konietzka
Does the parent process consumes the error- and outputstream from the child process? If under some OS the childprocess print out some errors/warning on stdout/stderr and the parent process is not consuming the streams, the childprocess will block and not reach System.exit();
父进程是否消耗来自子进程的错误和输出流?如果在某些操作系统下,子进程在 stdout/stderr 上打印出一些错误/警告并且父进程没有消耗流,则子进程将阻塞并且不会到达 System.exit();
回答by Romain Hippeau
Here are a couple of scenarios...
这里有几个场景......
Per the definition of a Thread in http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html
根据http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html 中线程的定义
...
...
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:
当 Java 虚拟机启动时,通常会有一个非守护线程(通常调用某个指定类的名为 main 的方法)。Java 虚拟机继续执行线程,直到发生以下任一情况:
1) The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place. 2) All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
1) Runtime 类的退出方法已被调用,安全管理器已允许退出操作发生。2) 所有不是守护线程的线程都已经死了,要么是从对 run 方法的调用返回,要么是通过抛出传播到 run 方法之外的异常。
Another possibility is if the method runFinalizersOnExit has been called. as per the documentation in http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.htmlDeprecated. This method is inherently unsafe. It may result in finalizers being called on live objects while other threads are concurrently manipulating those objects, resulting in erratic behavior or deadlock. Enable or disable finalization on exit; doing so specifies that the finalizers of all objects that have finalizers that have not yet been automatically invoked are to be run before the Java runtime exits. By default, finalization on exit is disabled. If there is a security manager, its checkExit method is first called with 0 as its argument to ensure the exit is allowed. This could result in a SecurityException.
另一种可能性是方法 runFinalizersOnExit 是否已被调用。根据http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html 中的文档 已弃用。这种方法本质上是不安全的。这可能会导致在其他线程同时操作这些对象时,在活动对象上调用终结器,从而导致不稳定的行为或死锁。退出时启用或禁用终结;这样做指定所有具有尚未自动调用的终结器的对象的终结器将在 Java 运行时退出之前运行。默认情况下,退出时的终结是禁用的。如果存在安全管理器,则首先调用其 checkExit 方法,并以 0 作为其参数,以确保允许退出。这可能会导致 SecurityException。
回答by Stephen C
I think all of the obvious causes have been provisionally covered; e.g. finalizers, shutdown hooks, not correctly draining standard output / standard error in the parent process. You now need more evidence to figure what is going on.
我认为所有明显的原因都已暂时涵盖;例如终结器、关闭挂钩、未正确排出父进程中的标准输出/标准错误。您现在需要更多证据来弄清楚发生了什么。
Suggestions:
建议:
Set up a Windows XP or Vista machine (or virtual), install the relevant JRE and your app, and try to reproduce the problem. Once you can reproduce the problem, either attach a debugger or send the relevant signal to get a thread dump to standard error.
If you cannot reproduce the problem as above, get one of your users to take a thread dump, and forward you the log file.
设置一台 Windows XP 或 Vista 机器(或虚拟机),安装相关的 JRE 和您的应用程序,并尝试重现问题。一旦您可以重现该问题,请附加调试器或发送相关信号以将线程转储到标准错误。
如果您无法重现上述问题,请让您的一位用户进行线程转储,并将日志文件转发给您。
回答by Dean Hiller
Another case not mentioned here is if the shutdown hooks hang on something so be careful in writing the shutdown hook code(and if a 3rd party library registered a shutdown hook that is hanging as well).
此处未提及的另一种情况是,如果关闭挂钩挂在某些东西上,因此在编写关闭挂钩代码时要小心(并且如果第 3 方库注册了一个也挂起的关闭挂钩)。
Dean
院长
回答by maxbit89
Hy i had the same problem, but the couse for me was i was using the remote Debugging(VmArgs: -Xdebug -Xrunjdwp:transport=dt_socket,address=%port%,server=y,suspend=y) when i disabled this the java.exe Process exited as expected.
Hy 我有同样的问题,但对我来说原因是我在禁用它时使用了远程调试(VmArgs:-Xdebug -Xrunjdwp:transport=dt_socket,address=%port%,server=y,suspend=y) java.exe 进程按预期退出。
回答by Molten Ice
This can happen if your code (or a library you use) has a shutdown hook or a finalizer that doesn't finish cleanly.
如果您的代码(或您使用的库)有一个关闭钩子或一个不能完全完成的终结器,就会发生这种情况。
A more vigorous (so should only be used in extreme cases!) way to force shutdown is by running:
一种更有力的(所以应该只在极端情况下使用!)强制关闭的方法是运行:
Runtime.getRuntime().halt(0);
回答by JavaTechnical
Check if there is a deadlock.
检查是否存在死锁。
For example,
例如,
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
System.exit(1);
and in the close()
并在 close()
public void close() {
// some code
System.exit(1);
}
The System.exit()
actually calls the shutdown hook and finalizers. So, if your shutdown hook calls the exit() again internally, for some reason (for example, when the close()
raises an exception for which you want to exit the program, then you will call exit()
there also). Like this..
在System.exit()
实际调用关机挂钩和终结。因此,如果您的关闭钩子出于某种原因在内部再次调用 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);
}
}
Though, it is a good practice to throw the exception, some may choose to log and exit.
虽然抛出异常是一个好习惯,但有些人可能会选择记录并退出。
Note, also, that
Ctrl+C
will also not work if there is a deadlock. Since it also calls the shutdown hook.
另请注意,
Ctrl+C
如果出现死锁,这也将不起作用。因为它还调用了关闭钩子。
Anyways, if it is the case, the problem can be solved by this workaround:
无论如何,如果是这种情况,可以通过以下解决方法解决问题:
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);
}
P.S: I feel that the above
exit()
version must actually be written in theSystem.exit()
method only (may be some PR for JDK?) Because, there is practically no point (at least from what I see) in entertaining a deadlock inSystem.exit()
PS:我觉得上面的
exit()
版本实际上必须只写在System.exit()
方法中(可能是JDK的一些PR?)因为,实际上没有任何意义(至少从我看来)在System.exit()