【问题标题】:What keeps java processes alive after jar execution?执行 jar 后,是什么让 java 进程保持活力?
【发布时间】:2011-05-12 08:02:31
【问题描述】:

我有一个从 Windows 命令提示符执行的 java jar 文件。代码正常完成(即按预期执行),但 java 进程继续运行。该应用程序是单线程的。代码完成后,我需要按 Ctrl-c 停止该过程以返回命令提示符。

我认为我可以在我的主要方法的末尾放置一个System.exit(0),这可能会解决这个问题,但我的印象是没有必要。 java进程在什么情况下在执行结束时保持活动状态?这是我的主要方法的外壳:

public static void main(String[] args) {
    try {
        //application code here
        Logger.log("Now finished");
    } catch (SomeExceptoin e) {
        Logger.error("Some error occured");
    }
}

记录器是我自己非常基本的静态类,它只是将消息转储到System.out.println()。 “现在完成”出现在控制台中,但进程继续运行。有任何想法吗?

编辑:根据要求,这里是记录器代码的全部荣耀(我确实警告过你:)

public class Logger {
   public static void logInfo(String logMessage)
   {
      System.out.println(timestamp() + logMessage);
   }
   private static String timestamp()
   {
      SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd hh:mm:ss");
      String timestamp = "[" + formatter.format(new Date()) + "] ";
      return timestamp;
   }
}

编辑编辑:我在上面的 try 中放置了一个 finally 块,并使用 instanceofTom 答案中链接中的线程转储代码。这是输出:

... [2010.11.18 11:22:57] 输出完成。所有处理现已完成。


线程名称:引用处理程序 java.lang.Object.wait(本机方法) java.lang.Object.wait(Object.java:485) java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)


线程名称:终结器 java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)


线程名称:信号调度器


线程名称:附加监听器


线程名称:Java2D Disposer java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) sun.java2d.Disposer.run(Disposer.java:125) java.lang.Thread.run(Thread.java:619)


线程名称:main java.lang.Thread.getStackTrace(Thread.java:1436) com.my.code.WorkloadManager.visit(WorkloadManager.java:124) com.my.code.WorkloadManager.visit(WorkloadManager.java:138) com.my.code.WorkloadManager.main(WorkloadManager.java:71) sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) java.lang.reflect.Method.invoke(Method.java:597) org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)


线程名称:com.google.inject.internal.Finalizer java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) com.google.inject.internal.Finalizer.run(Finalizer.java:114)


线程名称:AWT-Windows sun.awt.windows.WToolkit.eventLoop(本机方法) sun.awt.windows.WToolkit.run(WToolkit.java:291) java.lang.Thread.run(Thread.java:619)


线程名称:EventQueueMonitor-ComponentEvtDispatch java.lang.Object.wait(本机方法) java.lang.Object.wait(Object.java:485) com.sun.java.accessibility.util.ComponentEvtDispatchThread.run(EventQueueMonitor.java:616) **************** 线程名称:引用处理程序 java.lang.Object.wait(本机方法) java.lang.Object.wait(Object.java:485) java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)


线程名称:终结器 java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)


线程名称:信号调度器


线程名称:附加监听器


线程名称:Java2D Disposer java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) sun.java2d.Disposer.run(Disposer.java:125) java.lang.Thread.run(Thread.java:619)


线程名称:main java.lang.Thread.getStackTrace(Thread.java:1436) com.my.code.WorkloadManager.visit(WorkloadManager.java:124) com.my.code.WorkloadManager.visit(WorkloadManager.java:138) com.my.code.WorkloadManager.main(WorkloadManager.java:71) sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) java.lang.reflect.Method.invoke(Method.java:597) org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)


线程名称:com.google.inject.internal.Finalizer java.lang.Object.wait(本机方法) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) com.google.inject.internal.Finalizer.run(Finalizer.java:114)


线程名称:AWT-Windows sun.awt.windows.WToolkit.eventLoop(本机方法) sun.awt.windows.WToolkit.run(WToolkit.java:291) java.lang.Thread.run(Thread.java:619)


线程名称:EventQueueMonitor-ComponentEvtDispatch java.lang.Object.wait(本机方法) java.lang.Object.wait(Object.java:485) com.sun.java.accessibility.util.ComponentEvtDispatchThread.run(EventQueueMonitor.java:616)

【问题讨论】:

  • java程序是否调用了Runtime.exec()?
  • 我可能也会发布记录器代码。不知何故,一个孤立的线程必须持续存在。我知道没有其他方法可以发生这种情况。
  • 一个常见的原因是额外的非守护线程。尽管您的代码是单线程的,但您确定您调用的所有东西都没有创建非守护线程吗?
  • 我同意以前的 cmets。您可以尝试生成线程转储或使用“jps”工具。
  • @izb - 没有调用 Runtime.exec()。

标签: java process


【解决方案1】:

根据您的线程转储,看起来正在加载某个 Google 库(可能是 Guice 或 Guava)。

Thread name: com.google.inject.internal.Finalizer 
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) 
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) 
com.google.inject.internal.Finalizer.run(Finalizer.java:114) 

如果是这样,那么这个错误可能会导致问题:

这与终结器线程没有正确释放有关。发布了各种解决方法。

【讨论】:

  • 这也可能是我使用 Guice 进行依赖注入的原因(参见上面的 erickson 的帖子和相关的 cmets)。谢谢。
【解决方案2】:

具有 UI 的 Java 应用程序启动不是“守护程序”线程的事件处理线程。也就是说,只要这些线程正在运行,程序就不会终止——即使当前没有显示任何窗口。

您的菜单上有“退出”命令吗?它的Action 应该调用System.exit(0)。

【讨论】:

  • @Chris Knight - 您的线程转储显示有 AWT 事件线程正在运行。您正在使用的某些库可能使用图形工具包。例如,我尝试使用 SVG 库服务器端为 Web 应用程序生成图像,但该应用程序无法在 Google 应用程序引擎上运行,因为它在内部需要 AWT 库。
  • 好点。我没有考虑到这一点。深入挖掘我正在使用 JFreeChart 库来生成图形,这些图形随后会输出到磁盘,因此没有 UI,但在幕后涉及图形库。听起来像这样吗?
  • +1 基于线程转储添加的信息的良好捕获。
  • @Chris Knight - 可能就是这样。为了更智能地自动终止事件调度线程,已经做了很多工作,但是根据您运行的 Java 版本,您可能仍然会遇到问题。谷歌搜索,我发现一些报告称 JFree chart webapps“有时”没有正确关闭,但问题被关闭为“不可重现”。
【解决方案3】:

只要有一个或多个非守护线程在运行,Java 进程就会保持活动状态。

考虑使用位于 Java JDK 的 bin 目录中的 JVisualVM 并将其附加到您的 Java 程序中。在那里,您将能够分析活动线程及其状态以及执行线程转储。

【讨论】:

    【解决方案4】:

    如果您的代码中的一切看起来都正常,则很可能有一个孤立线程仍在运行。

    代码here描述了如何列出当前在JVM中运行的所有线程

    或者,您可以使用分析器或带有内置分析器的 IDE 来查看正在运行的线程。

    如果您能告诉我们在您的代码完成时哪些线程仍在运行,这将有助于回答这个问题; 即使你的代码可能是单线程的,JVM 中可能仍然会运行其他线程,例如垃圾收集线程(尽管 GC 线程不会阻止 JVM 退出)

    【讨论】:

    • 线程转储添加到我的问题基于链接中的代码被放置为我的主要方法中的最后一个可执行代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-22
    • 2014-05-19
    • 2012-09-08
    相关资源
    最近更新 更多