java - log - thread dump analyzer
Volcado de subprocesos de Java: subproceso BLOQUEADO sin "esperando para bloquear..." (4)
Tengo dificultades para comprender el volcado de hilos que obtuve de jstack para una aplicación web Spring MVC que se ejecuta en Tomcat 6 (java 1.6.0_22, Linux).
Veo hilos de bloqueo (que causan que otros hilos esperen) que están bloqueados, sin embargo, el volcado de hilo no me dice por qué o para qué monitor están esperando.
Ejemplo:
"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)
...
Es decir, me estoy perdiendo la línea "esperando para bloquear ..." en el seguimiento de la pila. Aparentemente, el hilo bloquea un objeto de Clase, pero no veo por qué el hilo está bloqueado.
El subproceso no contiene ninguna sugerencia para interbloqueos.
¿Qué puedo hacer para identificar el monitor de bloqueo?
Gracias Oliver
Aparentemente, la situación en la que observamos este tipo de subprocesos bloqueados estaba relacionada con un gran consumo de memoria y, por lo tanto, una recolección masiva de basura.
Esta pregunta problema de bloqueo de Java: ¿Por qué JVM bloquearía los subprocesos en muchas clases / métodos diferentes? describe una situación similar, así que creo que estos hilos simplemente fueron bloqueados por el recolector de basura.
(De todos modos, después de resolver el problema de memoria, este problema con los subprocesos de bloqueo desapareció).
Este es un error estético en la JVM HotSpot de Oracle: en el seguimiento de tu pila donde lo veas - locked <0x00007f3e9a0b3830>
en realidad debería decir - waiting to lock <0x00007f3e9a0b3830>
Ver este error para más detalles.
Tuve un problema similar ahora usando un applet en Google Chrome.
En breve:
- Los subprocesos BLOQUEADOS se pueden bloquear cuando la VM necesita cargar una clase.
- Cuando el proceso de carga de la clase en sí está bloqueado por algo, se puede producir una congelación de toda la aplicación.
En detalle:
Tuve el siguiente escenario:
- Estoy usando un applet en Chrome con codebase = carpeta para archivos de clase individuales (sin jarra)
- El sitio web pasa eventos de enfoque al applet usando LiveConnect
- Las llamadas JS entrantes están utilizando un
Executor
connew Runnable() ...
para desvincular las llamadas con el fin de reducir los tiempos de espera y, por lo tanto, se cuelga en JS. - ¡Ahí es donde ocurrió el problema!
Explicación:
- El
new Runnable()
es una clase interna anónima que no se cargó antes de que ocurriera la llamada JS. - La llamada JS, por lo tanto, activa la carga de clases.
- Pero ahora el cargador de clases está bloqueado porque necesita hablar con el navegador (supongo) a través de la misma cola o mecanismo que está procesando la llamada entrante de JS.
Aquí está el hilo bloqueado que está tratando de cargar la clase:
"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)
Como puede ver, está esperando un mensaje -> waitForMessage()
.
Al mismo tiempo, nuestra llamada entrante de JS está BLOQUEADA aquí:
"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)
Otros hilos adicionales fueron bloqueados de la misma manera. Supongo que todas las solicitudes de carga de clases subsiguientes fueron bloqueadas por el primer hilo de carga de clases bloqueado.
Como se mencionó anteriormente, mi conjetura es que el proceso de carga de clases está bloqueado por la llamada JS pendiente, que por sí misma está bloqueada por la clase que falta para cargar.
Soluciones:
- Activar la carga de todas las clases relevantes en el constructor del applet antes de que se puedan realizar llamadas desde JS.
- Podría ayudar si los archivos de clase no se cargan individualmente, sino desde un archivo jar. La teoría detrás de esto es: el cargador de clases no necesita hablar con el navegador para cargar las clases desde el archivo jar (que sería
- En combinación con 1 .: Use una clase de Proxy dinámico para envolver todas las llamadas entrantes JS y ejecútelas independientemente en un Executor .
Mi implementación para el # 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
}
}
Compruebe si el hilo del finalizador está bloqueado o en espera.
Durante un barrido de GC, el GC "detendrá el mundo" para realizar su limpieza. La definición de "mundo" depende del recolector de basura que se utilice y del contexto. Puede ser un pequeño grupo de hilos o todos ellos. Antes de recolectar oficialmente basura, GC invocará finalize () del objeto.
Si se encuentra en una situación indeseable en la que está implementando métodos finalizadores, es posible que el código de finalización no le permita terminar y el ''mundo'' se detiene.
Esto es más obvio cuando se ven muchos hilos bloqueados permanentemente por alguna fuerza mágica desconocida: busque el código donde ocurre el bloqueo y no tendrá sentido; no hay ningún código de bloqueo que se encuentre cerca de él y los volcados no divulgarán qué monitor está esperando porque no hay uno. El GC ha pausado los hilos.