Дамп потока Java: ЗАБЛОКИРОВАНО поток, не дожидаясь блокировки

Мне трудно понять дамп потока, который я получил из jstack для веб-приложения Spring MVC, работающего на Tomcat 6 (java 1.6.0_22, Linux).

Я вижу блокирующие потоки (которые заставляют другие потоки ждать), которые сами блокируются, однако дамп потока не сообщает мне, почему и какой монитор они ждут.

Пример:

"TP-Processor75" daemon prio=10 tid=0x00007f3e88448800 nid=0x56f5 waiting for monitor entry [0x00000000472bc000]
    java.lang.Thread.State: BLOCKED (on object monitor)
        at java.lang.Class.initAnnotationsIfNecessary(Class.java:3067)
        - locked <0x00007f3e9a0b3830> (a java.lang.Class for org.catapultframework.resource.ResourceObject)
        at java.lang.Class.getAnnotation(Class.java:3029)
        ...

Т.е. В трассировке стека отсутствует строка «Ожидает блокировки ...». По-видимому, поток блокирует объект класса, но я не понимаю, почему сам поток заблокирован.

Дамп потока не содержит никаких подсказок для тупиковых ситуаций.

Что я могу сделать, чтобы идентифицировать монитор запирания?

Спасибо Оливер


person Oliver    schedule 15.08.2011    source источник
comment
Вы видите какие-либо другие записи для ссылки 0x00000000472bc000?   -  person John Vint    schedule 15.08.2011
comment
Нет, не в той же свалке. 0x00000000472bc000 идентифицирует поток TP-Processor75, поэтому ожидаете ли вы многократного упоминания одного и того же потока в дампе?   -  person Oliver    schedule 15.08.2011
comment
Ну 0x00000000472bc000 указывает на монитор, в который поток ожидает входа. То есть другой поток ввел ссылку на монитор 0x00000000472bc000, и TP-Processor75 ожидает, что текущий удерживаемый поток выпустит 0x00000000472bc000   -  person John Vint    schedule 15.08.2011


Ответы (4)


Очевидно, ситуация, когда мы наблюдали такие заблокированные потоки, была связана с большим потреблением памяти и, следовательно, с массивной сборкой мусора.

Этот вопрос Проблема с блокировкой Java: Почему JVM блокирует потоки во многих разных классах / методах? описывает аналогичную ситуацию, поэтому я считаю, что эти потоки были просто заблокированы сборщиком мусора.

(В любом случае, после решения проблемы с памятью эта проблема с блокирующими потоками исчезла.)

person Oliver    schedule 26.08.2011

Убедитесь, что поток финализатора заблокирован или ожидает.

Во время очистки сборщика мусора сборщик мусора «остановит мир», чтобы выполнить очистку. Определение «мира» зависит от используемого сборщика мусора и контекста. Это может быть небольшой кластер потоков или все они. Перед официальным сбором мусора сборщик мусора вызовет finalize () объекта.

Если вы находитесь в нежелательной ситуации, когда вы реализуете методы финализатора, код завершения может блокировать его от завершения, и «мир» остается остановленным.

Это наиболее очевидно, когда вы видите, что множество потоков постоянно блокируется какой-то неизвестной магической силой: посмотрите код, в котором происходит блокировка, и он не будет иметь смысла; где-либо поблизости нет кода блокировки, и дампы не разглашают, какой монитор он ожидает, потому что его нет. Сборщик мусора приостановил потоки.

person user515655    schedule 15.02.2015

У меня была аналогичная проблема только сейчас, используя апплет в Google Chrome.

Короче:

  • ЗАБЛОКИРОВАННЫЕ потоки могут быть заблокированы, когда виртуальной машине нужно загрузить класс.
  • Когда процесс загрузки самого класса блокируется чем-то, может произойти зависание всего приложения.

Подробнее:

У меня был такой сценарий:

  1. Я использую апплет в Chrome с папкой codebase = для отдельных файлов классов (без банки)
  2. Веб-сайт передает фокус-события апплету с помощью LiveConnect.
  3. Входящие JS-вызовы используют Executor с new Runnable() ... для отсоединения вызовов, чтобы сократить время ожидания и, таким образом, зависают в JS.
  4. Вот где возникла проблема!

Объяснение:

  • new Runnable() - это анонимный внутренний класс, который не был загружен до того, как произошел JS-вызов.
  • Таким образом, вызов JS запускает загрузку класса.
  • Но теперь загрузчик классов заблокирован, потому что ему нужно разговаривать с браузером (я предполагаю) через ту же очередь или механизм, который обрабатывает входящий вызов JS.

Вот заблокированный поток, который пытается загрузить класс:

"Thread-20" daemon prio=4 tid=0x052e8400 nid=0x4608 in Object.wait() [0x0975d000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at sun.plugin2.message.Queue.waitForMessage(Unknown Source)
    - locked <0x29fbc5d8> (a sun.plugin2.message.Queue)
    at sun.plugin2.message.Pipe$2.run(Unknown Source)
    at com.sun.deploy.util.Waiter$1.wait(Unknown Source)
    at com.sun.deploy.util.Waiter.runAndWait(Unknown Source)
    at sun.plugin2.message.Pipe.receive(Unknown Source)
    at sun.plugin2.main.client.MessagePassingExecutionContext.doCookieOp(Unknown Source)
    at sun.plugin2.main.client.MessagePassingExecutionContext.getCookie(Unknown Source)
    at sun.plugin2.main.client.PluginCookieSelector.getCookieFromBrowser(Unknown Source)
    at com.sun.deploy.net.cookie.DeployCookieSelector.getCookieInfo(Unknown Source)
    at com.sun.deploy.net.cookie.DeployCookieSelector.get(Unknown Source)
    - locked <0x298da868> (a sun.plugin2.main.client.PluginCookieSelector)
    at sun.net.www.protocol.http.HttpURLConnection.setCookieHeader(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.writeRequests(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    - locked <0x2457cdc0> (a sun.net.www.protocol.http.HttpURLConnection)
    at com.sun.deploy.net.HttpUtils.followRedirects(Unknown Source)
    at com.sun.deploy.net.BasicHttpRequest.doRequest(Unknown Source)
    at com.sun.deploy.net.BasicHttpRequest.doGetRequestEX(Unknown Source)
    at com.sun.deploy.cache.ResourceProviderImpl.checkUpdateAvailable(Unknown Source)
    at com.sun.deploy.cache.ResourceProviderImpl.isUpdateAvailable(Unknown Source)
    at com.sun.deploy.cache.DeployCacheHandler.get(Unknown Source)
    - locked <0x245727a0> (a java.lang.Object)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    - locked <0x24572020> (a sun.net.www.protocol.http.HttpURLConnection)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at sun.plugin2.applet.Applet2ClassLoader.getBytes(Unknown Source)
    at sun.plugin2.applet.Applet2ClassLoader.access$000(Unknown Source)
    at sun.plugin2.applet.Applet2ClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.plugin2.applet.Applet2ClassLoader.findClass(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
    - locked <0x299726b8> (a sun.plugin2.applet.Applet2ClassLoader)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
    - locked <0x299726b8> (a sun.plugin2.applet.Applet2ClassLoader)
    at java.lang.ClassLoader.loadClass(Unknown Source)

Как видите, он ожидает сообщения -> waitForMessage().

В то же время здесь БЛОКИРУЕТСЯ наш входящий JS-вызов:

"Applet 1 LiveConnect Worker Thread" prio=4 tid=0x05231800 nid=0x1278 waiting for monitor entry [0x0770e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at MyClass.myMethod(MyClass.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.plugin.javascript.Trampoline.invoke(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.plugin.javascript.JSClassLoader.invoke(Unknown Source)
    at sun.plugin2.liveconnect.JavaClass$MethodInfo.invoke(Unknown Source)
    at sun.plugin2.liveconnect.JavaClass$MemberBundle.invoke(Unknown Source)
    at sun.plugin2.liveconnect.JavaClass.invoke0(Unknown Source)
    at sun.plugin2.liveconnect.JavaClass.invoke(Unknown Source)
    at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$DefaultInvocationDelegate.invoke(Unknown Source)
    at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo.doObjectOp(Unknown Source)
    at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$LiveConnectWorker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

Таким же образом были заблокированы и другие другие потоки. Я предполагаю, что все последующие запросы загрузки классов были заблокированы первым заблокированным потоком загрузки классов.

Как упоминалось ранее, я предполагаю, что процесс загрузки класса заблокирован ожидающим вызовом JS, который сам по себе блокируется отсутствующим классом для загрузки.

Решения:

  1. Запускайте загрузку всех соответствующих классов в конструкторе апплета до того, как какие-либо вызовы могут быть выполнены из JS.
  2. Это может помочь, если файлы классов загружаются не индивидуально, а из jar-файла. Теория, лежащая в основе этого: загрузчику классов не нужно разговаривать с браузером, чтобы загрузить классы из jar-файла (который был бы
  3. В сочетании с 1. Используйте динамический прокси, чтобы обернуть все входящие вызовы JS и запускать их независимо в Исполнитель.

Моя реализация для №3:

public class MyClass implements JsCallInterface
{

    private final JsCallInterface jsProxy;

    private final static interface JsCallInterface
    {
        public void myMethod1Intern(String param1, String param2);
    }

    private final class JsCallRunnable implements Runnable
    {

        private final Method method;
        private final Object[] args;

        private JsCallRunnable(Method method, Object[] args)
        {
            this.method = method;
            this.args = args;
        }

        public void run()
        {
            try
            {
                method.invoke(MyClass.this, args);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public MyClass()
    {
        MyUtilsClass.class.getName(); // load class
        JsCallRunnable.class.getName(); // load class
        InvocationHandler jsCallHandler = new InvocationHandler()
        {

            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
            {
                MyUtilsClass.executeInExecutor(new JsCallRunnable(method, args));
                return null;
            }

        };
        jsProxy = (JsCallInterface) Proxy.newProxyInstance(MyClass.class.getClassLoader(), new Class<?>[] { JsCallInterface.class }, jsCallHandler);
    }

    public void myMethod1(String param1, String param2)
    {
        jsProxy.myMethod1Intern(param1, param2);
        // needs to be named differently than the external method or else the proxy will call this method recursively
        // alternatively the target-class in "method.invoke(MyClass.this, args);" could be a different instance of JsCallInterface
    }

    public void myMethod1Intern(String param1, String param2)
    {
        // do actual work here
    }
}
person Frederic Leitenberger    schedule 06.03.2014

Это косметическая ошибка в Oracle HotSpot JVM - в трассировке стека, где вы видите - locked <0x00007f3e9a0b3830>, на самом деле должно быть указано - waiting to lock <0x00007f3e9a0b3830>.

Подробнее см. об этой ошибке.

person rxg    schedule 07.04.2014