Дамп Java-потока: BLOCKED thread без «ожидания блокировки ...» - PullRequest
20 голосов
/ 15 августа 2011

У меня возникают трудности с пониманием дампа потока, полученного из 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)
        ...

Т.е. я пропускаю "ожидание блокировки ... "строка в трассировке стека.Очевидно, что поток блокирует объект Class, но я не понимаю, почему сам поток заблокирован.

Дамп потока не содержит подсказок для взаимоблокировок.

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

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

Ответы [ 4 ]

10 голосов
/ 26 августа 2011

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

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

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

5 голосов
/ 15 февраля 2015

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

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

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

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

2 голосов
/ 06 марта 2014

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

Короче говоря:

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

Подробно:

У меня был следующий сценарий:

  1. Я использую апплет в Chrome с папкой codebase = для отдельных файлов классов (без jar)
  2. Сайт передает focus-события апплету с помощью 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 .: Используйте динамический класс Proxy , чтобы обернуть все входящие вызовы JS и выполнить их независимо в Executor .

Моя реализация для # 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
    }
}
1 голос
/ 07 апреля 2014

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

См. эту ошибку для получения более подробной информации.

...