2011-08-15 5 views
18

스레드 덤프를 이해하는 데 어려움이 있습니다. jstack에서 Tomcat 6 (Java 1.6.0_22, Linux)에서 실행되는 스프링 MVC 웹 응용 프로그램을 사용하고 있습니다. .Java 스레드 덤프 : "잠금 대기 중 ..."이 아닌 블로킹 된 스레드

블로킹 스레드 (다른 스레드가 대기하도록 함)가 스스로 차단되는 것을 볼 수 있지만 스레드 덤프는 왜 또는 어떤 모니터가 대기하는지 알려주지 않습니다.

예 :

"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) 
     ... 

즉, 스택 추적에서 "대기 잠금 ..."행이 누락되었습니다. 분명히 스레드가 클래스 개체를 잠그지 만, 스레드 자체가 차단 된 이유를 알 수 없습니다.

thread-dump에는 교착 상태에 대한 힌트가 없습니다.

잠금 모니터를 식별하려면 어떻게해야합니까?

감사합니다, 올리버

+0

참조 용 0x00000000472bc000에 대한 다른 항목이 있습니까? –

+0

아니요, 같은 덤프에 없습니다. 0x00000000472bc000은 "TP-Processor75"스레드를 식별하므로 덤프 내의 동일한 스레드에 대해 여러 번 언급 할 예정입니까? – Oliver

+0

글쎄 0x00000000472bc000은 스레드가 입력 대기중인 모니터를 나타냅니다. 다른 스레드가 모니터 참조 0x00000000472bc000을 입력했으며 TP-Processor75가 현재 보유 스레드가 0x00000000472bc000을 릴리스 할 때까지 기다리고 있습니다. –

답변

10

우리가 차단 된 스레드의 이러한 종류의 무거운 메모리 소비하기 때문에 대규모 쓰레기 수집 관련이 있었다 관찰 분명히 상황.

이 질문은 Java blocking issue: Why would JVM block threads in many different classes/methods?과 비슷한 상황을 설명하므로 이러한 스레드는 단순히 가비지 수집기에 의해 차단되었다고 생각합니다.

(어쨌든, 메모리 문제를 해결 한 후 차단 스레드가이 문제가 사라졌다.)

2

난 그냥 지금 구글 크롬에서 애플릿을 사용하여 비슷한 문제가 있었다. 한마디로

:

  • 차단 된 스레드가 VM가 클래스를로드 할 필요가있을 때 차단 될 수 있습니다.
  • 클래스 자체를로드하는 프로세스가 무언가에 의해 차단되면 전체 앱에 대해 고정이 발생할 수 있습니다. 세부 사항에

는 :

  1. 내가 코드베이스와 크롬 = 하나의 클래스 파일 폴더 (어떤 병)
  2. 를 애플릿을 사용하지 오전 :

    나는 다음과 같은 시나리오를했다 웹 사이트는 LiveConnect를 사용하여 애플릿에 포커스 이벤트를 전달합니다.

  3. 들어오는 JS 호출은 Executornew Runnable() ...을 사용하여 de- 대기 시간을 줄이기 위해 호출을 처리하므로 JS에 걸립니다.
  4. 문제가 발생한 곳입니다.

설명 :

  • new Runnable()는 JS 호출이 발생하기 전에로드되지 않은 annonymous 내부 클래스입니다.
  • 따라서 JS 호출은 클래스로드를 트리거합니다.
  • 하지만 클래스 로더는 들어오는 JS 호출을 처리하는 동일한 큐 또는 메커니즘을 통해 브라우저와 대화해야하기 때문에 차단됩니다 (추측). ->waitForMessage()

    "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) 
    

    당신이 메시지를 기다리는 볼 수 있듯이 : 여기

는 클래스의로드를 시도 차단 된 스레드입니다.

"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 호출에 의해 차단되며로드되지 않은 클래스에 의해 차단됩니다.

솔루션 : 호출하기 전에 애플릿의 생성자에서 모든 관련 클래스를로드

  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 
    } 
} 
0

이 오라클 핫스팟 JVM에서 화장품 버그 - 당신의 스택 추적에서 당신이 - locked <0x00007f3e9a0b3830>을 볼 실제로 - waiting to lock <0x00007f3e9a0b3830> 말을해야한다.

자세한 내용은 this bug을 참조하십시오.

3

마무리 자 스레드가 차단되었거나 대기 중인지 확인하십시오.

GC 스윕 중에 GC가 정리를 수행하기 위해 "세계를 멈추게"합니다. "월드"의 정의는 사용되는 가비지 컬렉터와 컨텍스트에 따라 다릅니다. 작은 스레드 클러스터이거나 모든 스레드 일 수 있습니다. GC는 공식적으로 가비지를 수집하기 전에 객체의 finalize()를 호출합니다.

finalizer 메서드를 구현하는 바람직하지 않은 상황에있는 경우 최종화 코드가 완료되지 못하게하고 '세계'를 중지 할 수 있습니다.

이것은 알 수없는 마법의 힘에 의해 영구적으로 차단되는 많은 스레드를 볼 때 가장 분명합니다. 블로킹이 발생하는 곳의 코드를 검색하면 의미가 없습니다. 그 근처에 발견되는 블로킹 코드는 없으며 덤프는 아무 것도 없으므로 대기중인 모니터를 누설하지 않습니다. GC가 스레드를 일시 중지했습니다.