2010-04-10 37 views
16

我有一个Java程序正在从另一个Java程序通过ProcessBuilder启动。 System.exit(0)是从子程序调用的,但对于我们的一些用户(在Windows上),与子项关联的java.exe进程不会终止。子程序没有关闭挂钩,也没有SecurityManager可能会阻止System.exit()终止虚拟机。我无法在Linux或Windows Vista上自己再现问题。到目前为止,这个问题的唯一报告来自两个Windows XP用户和一个Vista用户,使用两个不同的JRE(1.6.0_15和1.6.0_18),但他们每次都能够重现问题。什么会导致Java在System.exit()之后继续运行?

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

编辑1:我让用户安装JDK,以便我们可以从违规虚拟机获取线程转储。用户告诉我的是,只要点击菜单中的“退出”项,虚拟机进程就会从VisualVM中消失 - 但根据Windows任务管理器的说法,进程并没有终止,无论多长时间用户等待(分钟,小时),它永远不会终止。

编辑2:我已经确认Process.waitFor()在父程序中永远不会返回至少一个有问题的用户。因此,总结一下:孩子虚拟机似乎已经死了(VisualVM甚至没有看到它),但父母仍然认为该过程是活的,Windows也是这样。

回答

5

父进程具有一个螺纹 专用于消耗每个 孩子的STDOUT和STDERR(其通过 到一个日志 文件通过其输出)。到目前为止,我所看到的,这些都 正常工作,因为我们看到 所有的输出,我们期望在 日志

我与我的程序不会从任务经理的时候消失了类似的问题,看看我正在使用stdout/stderr。在我的情况下,如果我在调用system.exit()之前关闭正在监听的流,那么javaw.exe会挂起。奇怪的是,它没有写入流...

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

1

也许一个写得很糟的终结者?当我阅读主题行时,我首先想到了关闭挂钩。猜测:是否会捕获InterruptedException并继续运行的线程阻止退出进程?

在我看来,如果问题是可重复的,您应该能够附加到JVM并获得显示挂起的线程列表/堆栈跟踪。

你确定这个孩子还在运行吗?它不仅仅是一个没有反应的僵尸程序?

+0

如果它是一个终结,那就不是一个在我们的代码---我们不”没有任何。我确定孩子仍在运行,因为我们看到两个Java进程,并且用户在他的系统上没有任何其他使用Java的程序。我有点希望避免要求他安装JDK,所以我们可以使用jstack,但我认为现在可能是最好的方法。 – uckelman 2010-04-10 21:24:33

+0

我的意思是关于子进程,它是否还有线程?它是否仍然对输入做出反应?它是否仍然有打开的手柄?我比Unix更熟悉Unix,但我知道在Unix操作系统中进程不会被清除,直到父进程收回它们,我认为它在Windows上是一样的。仅仅因为它出现在顶部或TaskManager中并不意味着它仍然活跃。 当然,这并不能解释为什么它只发生在某些用户身上...... – 2010-04-11 01:53:18

+1

默认情况下,终结器不在退出时运行;请参阅http://java.sun.com/javase/6/docs/api/java/lang/System.html#runFinalizersOnExit(boolean) – 2010-04-11 04:01:21

1

父进程是否消耗子进程的错误输出流? 如果在某个操作系统下,childprocess会在stdout/stderr上打印出一些错误/警告,并且父进程没有使用这些流,则该子进程会阻塞并且无法到达System.exit();

+0

父进程有一个线程专用于使用每个孩子的STDOUT和STDERR(将输出传递给日志文件)。就我所知,这些工作正常,因为我们看到了所有我们期望在日志中看到的输出。 – uckelman 2010-04-10 21:51:36

3

这里有一对夫妇的情景......

每一个线程的定义http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html

...

当Java虚拟机启动时,通常有一个单一的非-daemon线程(通常调用一些指定类的main方法)。 Java虚拟机继续执行线程,直到出现以下任一情况:

1)类Runtime的退出方法已被调用且安全管理器已允许执行退出操作。 2)所有不是守护进程线程的线程都已经死了,要么从调用返回到run方法,要么抛出一个超出run方法传播的异常。

另一种可能是如果方法runFinalizersOnExit已被调用。根据http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html中的文档 已弃用。这种方法本质上是不安全的。这可能会导致终结器在活动对象上被调用,而其他线程同时操作这些对象,从而导致不稳定的行为或死锁。 在退出时启用或禁用终止;这样做指定了所有具有尚未自动调用终结器的对象的终结器将在Java运行时退出之前运行。默认情况下,退出时的终止被禁用。 如果存在安全管理器,则首先使用0调用其checkExit方法作为其参数,以确保允许退出。这可能会导致SecurityException。

+0

我们没有任何对'runFinalizersOnExit'的调用,也没有'SecurityManager',所以我认为这不是原因。 – uckelman 2010-04-10 21:53:43

0

我认为所有显而易见的原因都已被临时覆盖;例如终结器,关闭挂钩,不正确地排除父进程中的标准输出/标准错误。你现在需要更多的证据来确定发生了什么。

建议:

  1. 设置Windows XP或Vista机器(或虚拟的),安装相关的JRE和您的应用程序,并尝试重现该问题。一旦你可以重现这个问题,或者附加一个调试器或者发送相关信号来获得线程转储到标准错误。

  2. 如果您无法重现上述问题,请让您的一位用户进行线程转储,然后转发您的日志文件。

+0

我无法在Vista上自己复制它,并且用户无法从VisualVM获取线程转储,因为VM似乎不再活着。 – uckelman 2010-04-11 17:13:17

0

这里没有提到的另一种情况是,如果关闭钩挂在某些东西上,那么在编写关闭钩子代码时(以及第三方库注册挂起的关闭挂钩时)要小心。

院长

1

海兰我有同样的问题,但对我来说是职高我用的是远程调试(VmArgs:-Xdebug -Xrunjdwp:交通= dt_socket,地址=%端口%,服务器= Y,暂停= Y)当我禁用此java.exe进程退出按预期。

0

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

更有力的(所以应该只有在极端情况下使用!)的方式来强制关闭是通过运行:

Runtime.getRuntime().halt(0); 
相关问题