Java thread dump: BLOCKED thread без "ожидания блокировки..."
У меня возникают трудности с пониманием дампа потока, который я получил из jstack для веб-приложения MVC Spring, работающего на 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)
...
т.е. Мне не хватает строки "Ожидание блокировки..." в трассировке стека. По-видимому, поток блокирует объект класса, но я не понимаю, почему сам поток заблокирован.
Нисходящий дамп не содержит никаких подсказок для тупиков.
Что я могу сделать, чтобы идентифицировать монитор блокировки?
Спасибо,
Оливер
Ответы
Ответ 1
По-видимому, ситуация, когда мы наблюдали эти блокированные потоки, была связана с интенсивным потреблением памяти и, следовательно, с массивной сборкой мусора.
Этот вопрос Проблема блокировки Java: почему JVM блокирует потоки во многих разных классах/методах? описывает аналогичную ситуацию, поэтому я считаю, что эти потоки были просто заблокированы сборщик мусора.
(В любом случае, после решения проблемы с памятью эта проблема с блокирующими потоками исчезла.)
Ответ 2
Проверьте, заблокирован ли поток финализатора или ожидает.
Во время развертки GC GC "остановит мир", чтобы выполнить очистку. Определение "мир" зависит от используемого сборщика мусора и контекста. Это может быть небольшой кластер нитей или все из них. Перед тем, как официально собрать мусор, GC вызовет объект finalize().
Если вы находитесь в нежелательной ситуации, когда вы применяете методы финализатора, код завершения может блокировать его от завершения, а "мир" останавливается.
Это наиболее очевидно, когда многие потоки постоянно блокируются некоторой неизвестной магической силой: найдите код, где происходит блокировка, и это не имеет смысла; в любом месте рядом с ним нет кода блокировки, и дампы не будут разглашать, какой монитор он ожидает, потому что его нет. GC остановил потоки.
Ответ 3
У меня была аналогичная проблема только сейчас, используя Applet в Google Chrome.
Короче:
- Потоки BLOCKED могут быть заблокированы, когда виртуальной машине необходимо загрузить класс.
- Когда процесс загрузки самого класса блокируется чем-то, может произойти замораживание для всего приложения.
Подробно:
У меня был следующий сценарий:
- Я использую апплет в Chrome с папкой codebase = для отдельных файлов классов (без банок)
- Веб-сайт передает фокус-события апплетам с помощью LiveConnect
- Входящие JS-вызовы используют
Executor
с new Runnable() ...
для отсоединения вызовов, чтобы сократить время ожидания и, следовательно, зависает в JS.
- То, где возникла проблема!
Пояснение:
-
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-вызов BLOCKED здесь:
"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-вызовом, который сам по себе блокируется отсутствующим классом для загрузки.
Решения:
- Триггер загружает все соответствующие классы в конструкторе апплета, прежде чем любые вызовы могут быть сделаны из JS.
- Это может помочь, если файлы классов не загружаются отдельно, а из jar файла. Теория заключается в следующем: загрузчик классов не должен разговаривать с браузером для загрузки классов из jar файла (который будет
- В сочетании с 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
}
}
Ответ 4
Это косметическая ошибка в Oracle HotSpot JVM - в вашей трассе стека, где вы видите - locked <0x00007f3e9a0b3830>
, она должна сказать - waiting to lock <0x00007f3e9a0b3830>
.
Подробнее см. эту ошибку.