我的应用程序正在使用Gson 2.2
将POJOs
转换为JSON
。当我进行负载测试时,我偶然发现许多线程在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)
线程转储未显示任何持有[0x00007fe64df9a000] monitor
的线程。如何找出持有者?
第200行的Gson
代码看起来很简单:
// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
我在Linux
上使用JRE 1.8.0_91
2条答案
按热度按时间gmxoilav1#
tl;dr我认为您遇到了与GC相关的行为,其中线程被置于等待状态以允许垃圾收集。
我没有完整的真相,但我希望提供一些片段的见解。
首先要认识到的是,括号中的数字
[0x00007fe64df9a000]
不是监视器的地址。括号中的数字可以在转储中的所有线程中看到,即使是处于运行状态的线程。数字也不会改变。我的测试转储示例:我不确定这个数字是什么意思,但this page提示它是:
指向Java VM内部线程结构的指针。除非您正在调试实时Java VM或核心文件,否则通常不需要使用该指针。
虽然所解释的跟踪的格式有一点不同,所以我不确定我是否正确。
显示实际监视器的地址时转储的外观:
请注意跟踪中的
waiting to lock
行,并且监视器的地址与括号中的数字不同。我们看不到所涉及的监视器的地址这一事实表明,该监视器只存在于本机代码中。
其次,所涉及的Gson代码根本不包含任何同步。该代码只是向
ArrayList
添加了一个元素(假设没有进行字节码操作,也没有在低级别进行可疑操作)。也就是说,在此调用中看到等待标准同步监视器的线程是没有意义的。我发现some、indications,当有大量GC正在进行时,线程可以显示为等待监视器条目。
我编写了一个简单的测试程序,通过在数组列表中添加大量元素来重现它:
然后我对这个程序进行了线程转储。偶尔我会遇到下面的跟踪:
虽然与OP的跟踪不完全相同,但在不涉及同步的情况下使用线程
waiting on condition
是很有趣的。我在使用较小的堆时更频繁地遇到这种情况,这表明它可能与GC相关。另一种可能性是包含同步的代码是JIT编译的,这会阻止您看到监视器的实际地址。但是,我认为这种可能性较小,因为您在
ArrayList.add
上遇到过这种情况。如果是这种情况,我不知道有什么方法可以找到监视器的实际保持器。x6yk4ghg2#
如果你没有GC问题,那么实际上可能有一些线程已经获得了一个对象的锁,而阻塞线程正在等待获得同一个对象的锁。
例如
在线程转储中查找
waiting for monitor entry
条目。找到该条目后,您可以搜索已获取地址为<some_hex_address>
的对象上的锁的线程,示例如下所示-现在,您可以查看该线程的堆栈跟踪,以确定是哪行代码获取了它。