tl;博士我认为您遇到了与GC相关的行为,其中线程处于等待状态以允许垃圾回收。
我没有全部真相,但我希望提供一些见解。
首先要注意的是,括号中的数字 ,不是监视器的地址。对于转储中的所有线程,甚至包括处于运行状态的线程,都可以看到括号中的数字。该数字也不会更改。来自我的测试转储的示例:[0x00007fe64df9a000]
main" #1 prio=5 os_prio=0 tid=0x00007fe27c009000 nid=0x27e5c runnable [0x00007fe283bc2000]
java.lang.Thread.State: RUNNABLE
at Foo.main(Foo.java:12)
我不确定这个数字是什么意思,但这个页面暗示它是:
...指向 Java VM 内部线程结构的指针。除非您正在调试实时 Java VM 或核心文件,否则它通常无关紧要。
虽然所解释的跟踪的格式有点不同,所以我不确定我是否正确。
显示实际监视器的地址时转储的外观:
"qtp48612937-70" #70 prio=5 os_prio=0 tid=0x00007fbb845b4800 nid=0x133c waiting for monitor entry [0x00007fbad69e8000]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:233)
- waiting to lock <0x00000005b8d68e90> (a java.lang.Object)
请注意跟踪中的行,并且监视器的地址与括号中的数字不同。waiting to lock
我们无法看到所涉及的监视器的地址这一事实表明该监视器仅存在于本机代码中。
其次,所涉及的Gson代码根本不包含任何同步。代码只是将一个元素添加到 (假设没有执行字节码操作,并且在低级别上没有执行任何可疑操作)。也就是说,在此调用中看到等待标准同步监视器的线程是没有意义的。ArrayList
我发现了一些迹象,表明当有很多GC正在运行时,线程可以显示为等待监视器条目。
我编写了一个简单的测试程序,试图通过向数组列表中添加大量元素来重现它:
List<String> l = new ArrayList<>();
while (true) {
for (int i = 0; i < 100_100; i++) {
l.add("" + i);
}
l = new ArrayList<>();
}
然后我获取了这个程序的线程转储。有时我会遇到以下痕迹:
"main" #1 prio=5 os_prio=0 tid=0x00007f35a8009000 nid=0x12448 waiting on condition [0x00007f35ac335000]
java.lang.Thread.State: RUNNABLE
at Foo.main(Foo.java:10) <--- Line of l.add()
虽然与OP的跟踪不同,但在不涉及同步时拥有线程很有趣。我更频繁地使用较小的堆来体验它,这表明它可能与GC相关。waiting on condition
另一种可能性是包含同步的代码已经过 JIT 编译,从而阻止您查看监视器的实际地址。但是,我认为这不太可能,因为您在.如果是这样的话,我知道没有办法找出显示器的实际持有者。ArrayList.add