【问题标题】:Java "Thread-2" without stack prevents termination没有堆栈的 Java“Thread-2”防止终止
【发布时间】:2016-05-07 09:39:53
【问题描述】:

我有一个非常复杂的 java 程序,它不会终止。 eclipse 调试器显示了一个可以挂起的线程,但没有堆栈跟踪。 它被称为“线程 2”。

此线程的jstack -l 输出是:

"Thread-2" #17 prio=5 os_prio=0 tid=0x00007f1268002800 nid=0x3342 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

我在 Thread.start() 中添加了一个断点,但我找不到名为“Thread-2”的线程。 该线程仅在创建两个“AWT-Event-Queue”线程后出现。 我没有在我的程序中手动创建任何线程。

在主线程和所有其他线程退出,并且JFrame被释放后,以下线程仍然存在:

Thread [AWT-EventQueue-0] (Running) 
Thread [Thread-2] (Running) 
Thread [DestroyJavaVM] (Running)    

暂停VM时,存在以下线程:

Daemon System Thread [Signal Dispatcher] (Suspended)    
Daemon System Thread [Finalizer] (Suspended)    
Daemon System Thread [Reference Handler] (Suspended)    
Daemon System Thread [Java2D Disposer] (Suspended)  
Daemon System Thread [AWT-XAWT] (Suspended) 
Thread [AWT-EventQueue-0] (Suspended)   
Thread [Thread-2] (Suspended)   
Thread [DestroyJavaVM] (Suspended)  

如何获得有关此线程的更多信息,或允许它终止?

编辑 1:

根据eclipsepom.xml视图的Dependency Hierarchy,我使用以下第三方库:

guava 17.0 [compile]
hamcrest-core 1.3 [test]
junit 4.11 [test]
log4j-api 2.0-beta9 [compile]
log4j-core 2.0-beta9 [compile]

编辑 2:

按照https://stackoverflow.com/a/35128213/577485 中的建议为线程类的所有构造函数添加断点,我看到Thread-0Thread-1 是由log4j 创建的,但不是Thread-2。它仍然像以前一样出现,并且在构造时没有触发断点。

编辑 3:

现在它变得令人毛骨悚然。在线程上调用时,甚至 stop() 方法都不起作用。我将它添加到https://stackoverflow.com/a/35128149/577485 中给出的代码中。 至少System.exit(int) 仍然有效。但正如评论中所说,我不想使用它。

编辑 4:

关于我的系统的信息:

  • 我正在运行最新的稳定版 Ubuntu 15.10 Wily。我启用了 securityupdatesbackports 存储库。
  • 我的 JVM 版本是:

java version "1.7.0_91" OpenJDK Runtime Environment (IcedTea 2.6.3) (7u91-2.6.3-0ubuntu0.15.10.1) OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode)

编辑 5:

我使用直接从java.com下载的Java版本jre-8u71-linux-x64执行程序,但错误仍然存​​在。 jstack -l 显示相同的奇怪线程。请注意,该程序仍然是使用较旧的 java 版本构建的。 编辑: 使用 oracle.com 的 java8u72 编译后,我得到了相同的行为。

编辑 6:

我遍历了所有线程字段,这是输出。我无法从这些字段中得到任何提示,线程甚至没有目标。

name: [C@6b67034
priority: 5
threadQ: null
eetop: 140274638530560
single_step: false
daemon: false
stillborn: false
target: null
group: java.lang.ThreadGroup[name=main,maxpri=10]
contextClassLoader: null
inheritedAccessControlContext: java.security.AccessControlContext@0
threadInitNumber: 3
threadLocals: null
inheritableThreadLocals: null
stackSize: 0
nativeParkEventPointer: 0
tid: 17
threadSeqNumber: 20
threadStatus: 5
parkBlocker: null
blocker: null
blockerLock: java.lang.Object@16267862
MIN_PRIORITY: 1
NORM_PRIORITY: 5
MAX_PRIORITY: 10
EMPTY_STACK_TRACE: [Ljava.lang.StackTraceElement;@453da22c
SUBCLASS_IMPLEMENTATION_PERMISSION: ("java.lang.RuntimePermission" "enableContextClassLoaderOverride")
uncaughtExceptionHandler: null
defaultUncaughtExceptionHandler: null
threadLocalRandomSeed: 0
threadLocalRandomProbe: 0
threadLocalRandomSecondarySeed: 0

编辑 7:

Threadname 字段添加了一个观察点。它只能由我的分析代码访问,并且似乎从未编写过......

编辑 8:

jstack -F -m 为我的程序抛出错误:

Attaching to process ID 10973, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.71-b15
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.tools.jstack.JStack.runJStackTool(JStack.java:140)
    at sun.tools.jstack.JStack.main(JStack.java:106)
Caused by: java.lang.RuntimeException: Unable to deduce type of thread from address 0x00007ff68000c000 (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread, or SurrogateLockerThread)
    at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:169)
    at sun.jvm.hotspot.runtime.Threads.first(Threads.java:153)
    at sun.jvm.hotspot.tools.PStack.initJFrameCache(PStack.java:200)
    at sun.jvm.hotspot.tools.PStack.run(PStack.java:71)
    at sun.jvm.hotspot.tools.PStack.run(PStack.java:58)
    at sun.jvm.hotspot.tools.PStack.run(PStack.java:53)
    at sun.jvm.hotspot.tools.JStack.run(JStack.java:66)
    at sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
    at sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
    at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
    at sun.jvm.hotspot.tools.JStack.main(JStack.java:92)
    ... 6 more
Caused by: sun.jvm.hotspot.types.WrongTypeException: No suitable match    for type of address 0x00007ff68000c000
    at sun.jvm.hotspot.runtime.InstanceConstructor.newWrongTypeException(InstanceConstructor.java:62)
    at sun.jvm.hotspot.runtime.VirtualConstructor.instantiateWrapperFor(VirtualConstructor.java:80)
    at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:165)
    ... 16 more

奇怪线程的类名是java.lang.Thread

我不使用任何命令行参数来执行程序。添加-Dlog4j2.disable.jmx=true 选项会为奇怪的线程提供名称Thread-1

我将log4j 更新为 2.5,当给出-Dlog4j2.disable.jmx=true 选项时,奇怪的线程现在名称为Thread-0,而在没有给出时名称为Thread-1

编辑 9:

完全删除 log4j,错误仍然存​​在。该线程现在称为Thread-0


Here 的项目,如果有帮助的话。

【问题讨论】:

  • 这似乎是您在应用程序中使用的第三方库,手动创建新线程,它不是守护线程,而是用户线程。您使用哪些库?
  • 我克隆了项目并直接从 IntelliJ Idea 在 Oracle JDK 1.8u72 上运行它,在 Mac OS X 上运行。当我关闭框架时,JVM 在 master 和 @ 上都没有问题地终止987654357@ 分支(我应该使用哪一个?)。当程序运行时,我永远看不到Thread-2 线程。您使用的是哪个 JVM 和操作系统?可以尝试在最新的 JDK 8 上运行吗?
  • 能否包含一个线程转储,包括非结束线程的调用堆栈?它的要点很好。
  • 您可以尝试获取 Java 堆转储并调查那里的线程堆栈。可能会给你带来新的结果。
  • 关于调试的更多建议。在线程的 init 方法中放置一个调试断点,它将分配给 name 和 setName 方法。然后您可以看到 Thread-2 的创建位置(如果有的话)。可以告诉您有关它的用途以及它正在执行的可运行文件的更多信息

标签: java eclipse multithreading debugging stack-trace


【解决方案1】:

不确定这对您来说是否足够,但以下代码将允许您尝试使用其名称interrupt 任何Thread

    //Set of current Threads
    Set<Thread> setOfThread = Thread.getAllStackTraces().keySet();

    //Iterate over set to find yours
    for(Thread thread : setOfThread){
        if (thread.getName().equals("Thread-2")) {
            thread.interrupt();
            break;
        }
    }

此外,请查看来自 JavaSpecialists 的 article,它试图根据 Thread 的构造函数调用安全管理器这一事实来识别线程的创建者。如果我们将自定义SecurityManager 添加到我们的系统中,我们可能会跟踪线程的发起者。

【讨论】:

  • 感谢您的回答。不幸的是,中断没有任何改变,线程保持RUNNABLE并且仍然没有堆栈跟踪。
【解决方案2】:

我不明白为什么Thread.start() 上的断点不起作用,但您也可以尝试通过在线程构造函数或(内部)Thread.init() 上设置断点来拦截线程 >>creation

线程具有名称Thread-2 的事实意味着它是由生成默认线程名称的构造函数之一创建的。这表明它不是由 JVM 或标准 Java 类库创建的。它还缩小了可能用于创建它的构造函数。

我怎样才能获得有关此线程的更多信息...

除了设置断点,我想不出任何办法。

...还是让它终止?

如果您能找到它的创建位置,您应该可以使用setDaemon(true) 将其标记为守护线程。但是,这需要在线程启动之前完成。

另一种可能性是通过遍历ThreadGroup 树然后在其上调用Thread.interrupt() 来找到线程。 (Thread.getAllStackTraces() 是跟踪线程对象的另一种方式。)但是,不能保证线程会“尊重”中断并关闭。

最后,您可以拨打System.exit(...)


更新

我提到线程可能不尊重interrupt(),我对stop() 不起作用并不感到惊讶。 (它已被弃用,甚至可能不会在某些平台上实现。)

但是,如果您设法实现了真正发现神秘线程的代码,您可以四处寻找Thread 子类或实例化它的Runnable。如果您可以打印出完全限定的类名,那将为您提供关于它来自何处的重要线索。 (假设您仍然没有成功使用断点,那么您可能需要使用“讨厌的”反射从线程的私有 target 字段中提取可运行对象。)

【讨论】:

  • 感谢您的信息,它已经帮助了很多。我对解释发生了什么的问题添加了一个编辑。 System.exit() 不是一个选项,因为它只会掩盖错误。
  • 感谢您的意见。目标为空。我猜这很奇怪。我在问题中添加了所有字段的列表。
  • 这没有意义。如果您在具有nullRunnablejava.lang.Thread 上调用Thread.start(),则线程应立即终止。我想知道您是否看到了一些本机/“不安全”代码破坏事物的结果......
  • 是的,我想我目前正在寻找较低级别的错误。特别是因为在 Mac 上,一切似乎都很好。我的下一个想法是学习 gdb,并用它来发现问题。
【解决方案3】:

首先,您应该将 _exit 标志更改为 volatile,因为它是从一个线程(您的 main 方法)读取,并由另一个线程(JCF/Swing 事件处理程序)写入,所以您的主线程可能是没有得到“新鲜”的价值。具体来说:线程可能会将字段保存到 CPU 寄存器,而不是在循环时从内存中重新加载它。 'volatile' 会阻止这种行为:

private volatile boolean _exit;

但是,根据您的堆栈跟踪,我认为这不是您的问题,因为我们在那里看不到您的主要方法。但无论如何都应该这样做,这只是一个好习惯。

假设这不能解决问题,我猜您的问题是您至少还有一个未处理的其他窗口(@98​​7654324@ 除外)。 AWT 线程在所有 Windows 都被释放之前不会停止。

把它放在你的主要方法的末尾:

System.out.println(Arrays.toString(Window.getWindows()))

我猜你在那里看到的不仅仅是你的DrawFrame。如果我猜的话,我会说UISettingsFrame 在那里,没有被处置。

【讨论】:

  • 感谢您的意见。可悲的是,这些都没有帮助。窗口在 main 方法结束时无法显示。
  • 我不确定您所说的“不可显示”是什么意思。隐藏所有窗口是不够的。它们必须被disposed,否则 AWT 线程将永远不会结束。在你的主要结尾试试这个:for (Window w: Window.getWindows()) if (w.isValid()) throw new RuntimeException("Fix this");。如果抛出该异常,您需要确保该窗口正在被释放(在窗口自己的代码中,我不建议在 main 中迭代和释放,这会有点草率)。
  • 窗口也无效。尽管如此,我还是在设置框架为空之前添加了处理它。
猜你喜欢
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
  • 2016-06-21
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多