如何找出哪个线程容纳显示器?

bed*_*rin 27 java multithreading thread-dump gson

我的应用程序Gson 2.2用于转换POJOsJSON.当我进行负载测试时,我偶然发现Gson构造函数中阻塞了很多线程:

"http-apr-28201-exec-28" #370 daemon prio=5 os_prio=0 tid=0x0000000001ee7800 nid=0x62cb waiting for monitor entry [0x00007fe64df9a000]
    java.lang.Thread.State: BLOCKED (on object monitor)
    at com.google.gson.Gson.<init>(Gson.java:200)
    at com.google.gson.Gson.<init>(Gson.java:179)
Run Code Online (Sandbox Code Playgroud)

线程转储不显示任何线程持有[0x00007fe64df9a000] monitor. 我怎样才能知道谁拥有它?

Gson第200行的代码看起来很无辜:

// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
Run Code Online (Sandbox Code Playgroud)

我使用JRE 1.8.0_91Linux

K E*_*son 13

tl; dr我认为你正在遇到与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)
Run Code Online (Sandbox Code Playgroud)

我不确定这个数字是什么意思,但这个页面暗示它是:

...指向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)
Run Code Online (Sandbox Code Playgroud)

注意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<>();
}
Run Code Online (Sandbox Code Playgroud)

然后我接受了这个程序的线程转储.偶尔我遇到了以下痕迹:

"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()
Run Code Online (Sandbox Code Playgroud)

虽然与OP的跟踪不完全相同,但waiting on condition在没有涉及同步时有一个线程是很有趣的.我用较小的堆更频繁地体验它,表明它可能与GC有关.

另一种可能是包含同步的代码已经过JIT编译,这会阻止您查看监视器的实际地址.但是,我认为这是不太可能的,因为你经历过ArrayList.add.如果是这种情况,我知道无法找出显示器的实际支架.