Подтвердить что ты не робот

Как узнать, какая нить содержит монитор?

Мое приложение использует 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. Как узнать, кто его удерживает?

Gson code в строке 200 выглядит довольно невинно:

// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);

Я использую JRE 1.8.0_91 на Linux

4b9b3361

Ответ 1

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)

Я не уверен, что означает номер, но эта страница намекает, что это:

... указатель на структуру внутреннего потока 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 (при условии, что обработка байт-кода не выполняется, и ничего нечего делать на низком уровне). I.e., было бы бессмысленно видеть поток, ожидающий стандартного монитора синхронизации при этом вызове.

Я нашел несколько, показания, что потоки могут отображаться как ожидающие записи монитора, когда есть много 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, интересно иметь поток waiting on condition, когда синхронизация не задействована. Я испытывал это чаще с меньшей кучей, указывая, что это может быть связано с GC.

Другая возможность может заключаться в том, что код, содержащий синхронизацию, был скомпилирован JIT и не позволяет вам видеть фактический адрес монитора. Однако я думаю, что это менее вероятно, так как вы испытываете это на ArrayList.add. Если это так, я не знаю, как узнать фактического владельца монитора.