"ClassLoader deadlock when compiling JSP pages in 6.0.26 " 이라는 버그 질라가 떳다..
https://issues.apache.org/bugzilla/show_bug.cgi?id=48903
6.0.26 버젼의 WEB Class Loader 클래스에 약간의 문제가 있다..
아직 문제는 없지만, 문제가 일어날 소지가 있으므로.. 반드시 내용을 적어둔..
위 링크를 보면, Deadlock를 재현하여 Thread dump 를 떠 놓은 것이 있다. 서로가 Deadlock에 빠져있다.
헐..
Found one Java-level deadlock:
=============================
"Handling GET /job/Project1Trunk/ : http-8080-40":
waiting to lock monitor 0x000000005b013e50 (object 0x00002aaac2cc9b58, a java.net.URLClassLoader),
which is held by "Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22"
"Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22":
waiting to lock monitor 0x000000005b986958 (object 0x00002aaab4465798, a java.lang.String),
which is held by "Handling GET /job/Project2Trunk/emma/graph : http-8080-14"
"Handling GET /job/Project2Trunk/emma/graph : http-8080-14":
waiting to lock monitor 0x000000005b0169a0 (object 0x00002aaac266fc30, a org.apache.catalina.loader.WebappClassLoader),
which is held by "Handling GET /job/Project2Trunk/test/trend : http-8080-15"
"Handling GET /job/Project2Trunk/test/trend : http-8080-15":
waiting to lock monitor 0x000000005b986958 (object 0x00002aaab4465798, a java.lang.String),
which is held by "Handling GET /job/Project2Trunk/emma/graph : http-8080-14"
(1)Thread
"Handling GET /job/Project1Trunk/ : http-8080-40" daemon prio=10 tid=0x000000005c3e9000 nid=0x2a8f waiting for monitor entry [0x0000000045228000]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.misc.Unsafe.defineClass(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:45)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:381)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:377)
at sun.reflect.MethodAccessorGenerator.generateMethod(MethodAccessorGenerator.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:28)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
(2)Thread
"Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22":
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1394)
- waiting to lock <0x00002aaab4465798> (a java.lang.String)
at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
- locked <0x00002aaac2c3ed48> (a hudson.ClassicPluginStrategy$DependencyClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
- locked <0x00002aaac2cc9b58> (a java.net.URLClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
- locked <0x00002aaac2cc9b58> (a java.net.URLClassLoader)
(3) Thread
"Handling GET /job/Project2Trunk/emma/graph : http-8080-14" daemon prio=10 tid=0x000000005c359000 nid=0x29d4 waiting for monitor entry [0x000000004370f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.ClassLoader.checkCerts(ClassLoader.java:752)
- waiting to lock <0x00002aaac266fc30> (a org.apache.catalina.loader.WebappClassLoader)
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:488)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
(4)Thread
"Handling GET /job/Project2Trunk/test/trend : http-8080-15" daemon prio=10 tid=0x000000005b093000 nid=0x29d5 waiting for monitor entry [0x0000000043810000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1394)
- waiting to lock <0x00002aaab4465798> (a java.lang.String)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
- locked <0x00002aaac266fc30> (a org.apache.catalina.loader.WebappClassLoader)
=============================
"Handling GET /job/Project1Trunk/ : http-8080-40":
waiting to lock monitor 0x000000005b013e50 (object 0x00002aaac2cc9b58, a java.net.URLClassLoader),
which is held by "Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22"
"Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22":
waiting to lock monitor 0x000000005b986958 (object 0x00002aaab4465798, a java.lang.String),
which is held by "Handling GET /job/Project2Trunk/emma/graph : http-8080-14"
"Handling GET /job/Project2Trunk/emma/graph : http-8080-14":
waiting to lock monitor 0x000000005b0169a0 (object 0x00002aaac266fc30, a org.apache.catalina.loader.WebappClassLoader),
which is held by "Handling GET /job/Project2Trunk/test/trend : http-8080-15"
"Handling GET /job/Project2Trunk/test/trend : http-8080-15":
waiting to lock monitor 0x000000005b986958 (object 0x00002aaab4465798, a java.lang.String),
which is held by "Handling GET /job/Project2Trunk/emma/graph : http-8080-14"
(1)Thread
"Handling GET /job/Project1Trunk/ : http-8080-40" daemon prio=10 tid=0x000000005c3e9000 nid=0x2a8f waiting for monitor entry [0x0000000045228000]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.misc.Unsafe.defineClass(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:45)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:381)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:377)
at sun.reflect.MethodAccessorGenerator.generateMethod(MethodAccessorGenerator.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:28)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
(2)Thread
"Handling GET /job/Project2Trunk/disk-usage/graph : http-8080-22":
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1394)
- waiting to lock <0x00002aaab4465798> (a java.lang.String)
at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
- locked <0x00002aaac2c3ed48> (a hudson.ClassicPluginStrategy$DependencyClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
- locked <0x00002aaac2cc9b58> (a java.net.URLClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
- locked <0x00002aaac2cc9b58> (a java.net.URLClassLoader)
(3) Thread
"Handling GET /job/Project2Trunk/emma/graph : http-8080-14" daemon prio=10 tid=0x000000005c359000 nid=0x29d4 waiting for monitor entry [0x000000004370f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.ClassLoader.checkCerts(ClassLoader.java:752)
- waiting to lock <0x00002aaac266fc30> (a org.apache.catalina.loader.WebappClassLoader)
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:488)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
(4)Thread
"Handling GET /job/Project2Trunk/test/trend : http-8080-15" daemon prio=10 tid=0x000000005b093000 nid=0x29d5 waiting for monitor entry [0x0000000043810000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1394)
- waiting to lock <0x00002aaab4465798> (a java.lang.String)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
- locked <0x00002aaac266fc30> (a org.apache.catalina.loader.WebappClassLoader)
이 부분을 어떻게 풀었는지 확인했다.
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/ResourceEntry.java 2010/05/06 19:15:48 941867 +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/ResourceEntry.java 2010/05/06 19:17:49 941868 @@ -47,7 +47,7 @@ /** * Loaded class. */ - public Class loadedClass = null; + public volatile Class loadedClass = null; /**
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 2010/05/06 19:15:48 941867 +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 2010/05/06 19:17:49 941868 @@ -1432,102 +1432,121 @@ * * @exception ClassNotFoundException if the class was not found */ - public Class loadClass(String name, boolean resolve) + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - synchronized (name.intern()) { - if (log.isDebugEnabled()) - log.debug("loadClass(" + name + ", " + resolve + ")"); - Class clazz = null; - - // Log access to stopped classloader - if (!started) { - try { - throw new IllegalStateException(); - } catch (IllegalStateException e) { - log.info(sm.getString("webappClassLoader.stopped", name), e); - } + if (log.isDebugEnabled()) + log.debug("loadClass(" + name + ", " + resolve + ")"); + Class clazz = null; + + // Log access to stopped classloader + if (!started) { + try { + throw new IllegalStateException(); + } catch (IllegalStateException e) { + log.info(sm.getString("webappClassLoader.stopped", name), e); } - - // (0) Check our previously loaded local class cache - clazz = findLoadedClass0(name); + } + + // (0) Check our previously loaded local class cache + clazz = findLoadedClass0(name); + if (clazz != null) { + if (log.isDebugEnabled()) + log.debug(" Returning class from cache"); + if (resolve) + resolveClass(clazz); + return (clazz); + } + + // (0.1) Check our previously loaded class cache + clazz = findLoadedClass(name); + if (clazz != null) { + if (log.isDebugEnabled()) + log.debug(" Returning class from cache"); + if (resolve) + resolveClass(clazz); + return (clazz); + } + + // (0.2) Try loading the class with the system class loader, to prevent + // the webapp from overriding J2SE classes + try { + clazz = system.loadClass(name); if (clazz != null) { - if (log.isDebugEnabled()) - log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return (clazz); } - - // (0.1) Check our previously loaded class cache - clazz = findLoadedClass(name); - if (clazz != null) { - if (log.isDebugEnabled()) - log.debug(" Returning class from cache"); - if (resolve) - resolveClass(clazz); - return (clazz); + } catch (ClassNotFoundException e) { + // Ignore + } + + // (0.5) Permission to access this class when using a SecurityManager + if (securityManager != null) { + int i = name.lastIndexOf('.'); + if (i >= 0) { + try { + securityManager.checkPackageAccess(name.substring(0,i)); + } catch (SecurityException se) { + String error = "Security Violation, attempt to use " + + "Restricted Class: " + name; + log.info(error, se); + throw new ClassNotFoundException(error, se); + } } - - // (0.2) Try loading the class with the system class loader, to prevent - // the webapp from overriding J2SE classes + } + + boolean delegateLoad = delegate || filter(name); + + // (1) Delegate to our parent if requested + if (delegateLoad) { + if (log.isDebugEnabled()) + log.debug(" Delegating to parent classloader1 " + parent); + ClassLoader loader = parent; + if (loader == null) + loader = system; try { - clazz = system.loadClass(name); + clazz = loader.loadClass(name); if (clazz != null) { + if (log.isDebugEnabled()) + log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) { - // Ignore - } - - // (0.5) Permission to access this class when using a SecurityManager - if (securityManager != null) { - int i = name.lastIndexOf('.'); - if (i >= 0) { - try { - securityManager.checkPackageAccess(name.substring(0,i)); - } catch (SecurityException se) { - String error = "Security Violation, attempt to use " + - "Restricted Class: " + name; - log.info(error, se); - throw new ClassNotFoundException(error, se); - } - } + ; } - - boolean delegateLoad = delegate || filter(name); - - // (1) Delegate to our parent if requested - if (delegateLoad) { + } + + // (2) Search local repositories + if (log.isDebugEnabled()) + log.debug(" Searching local repositories"); + try { + clazz = findClass(name); + if (clazz != null) { if (log.isDebugEnabled()) - log.debug(" Delegating to parent classloader1 " + parent); - ClassLoader loader = parent; - if (loader == null) - loader = system; - try { - clazz = loader.loadClass(name); - if (clazz != null) { - if (log.isDebugEnabled()) - log.debug(" Loading class from parent"); - if (resolve) - resolveClass(clazz); - return (clazz); - } - } catch (ClassNotFoundException e) { - ; - } + log.debug(" Loading class from local repository"); + if (resolve) + resolveClass(clazz); + return (clazz); } - - // (2) Search local repositories + } catch (ClassNotFoundException e) { + ; + } + + // (3) Delegate to parent unconditionally + if (!delegateLoad) { if (log.isDebugEnabled()) - log.debug(" Searching local repositories"); + log.debug(" Delegating to parent classloader at end: " + parent); + ClassLoader loader = parent; + if (loader == null) + loader = system; try { - clazz = findClass(name); + clazz = loader.loadClass(name); if (clazz != null) { if (log.isDebugEnabled()) - log.debug(" Loading class from local repository"); + log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return (clazz); @@ -1535,30 +1554,10 @@ } catch (ClassNotFoundException e) { ; } - - // (3) Delegate to parent unconditionally - if (!delegateLoad) { - if (log.isDebugEnabled()) - log.debug(" Delegating to parent classloader at end: " + parent); - ClassLoader loader = parent; - if (loader == null) - loader = system; - try { - clazz = loader.loadClass(name); - if (clazz != null) { - if (log.isDebugEnabled()) - log.debug(" Loading class from parent"); - if (resolve) - resolveClass(clazz); - return (clazz); - } - } catch (ClassNotFoundException e) { - ; - } - } - - throw new ClassNotFoundException(name); } + + throw new ClassNotFoundException(name); + } @@ -2544,7 +2543,7 @@ if (clazz != null) return clazz; - synchronized (name.intern()) { + synchronized (this) { clazz = entry.loadedClass; if (clazz != null) return clazz;
--- tomcat/tc6.0.x/trunk/java/org/apache/jasper/servlet/JasperLoader.java 2010/05/06 19:15:48 941867 +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/servlet/JasperLoader.java 2010/05/06 19:17:49 941868 @@ -91,7 +91,7 @@ * * @exception ClassNotFoundException if the class was not found */ - public Class loadClass(final String name, boolean resolve) + public synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException { Class clazz = null; @@ -169,4 +169,4 @@ public final PermissionCollection getPermissions(CodeSource codeSource) { return permissionCollection; } -} \ No newline at end of file +}
Synchronized를 잘 정리했고, volatile을 써서 명확하게 Thread로 인한 optimization이 안되도록 했다.
병렬적으로 클래스로딩이 될 때, 문제가 꼬일 수 있는 부분이 패치한 것으로 보인다.
'java core' 카테고리의 다른 글
System.arraycopy보다 유연한 Arrays.copyOf 메서드 (0) | 2010.05.13 |
---|---|
String의 intern 메소드에 대해서 잘 쓴 글 (0) | 2010.05.13 |
병렬 쓰레드 프로그래밍시 Thread Safety 확인하는 습관 갖기 (1) | 2010.05.07 |
log4j와 java util logging의 차이 (0) | 2010.04.18 |
Desktop JVM 보안 문제 (0) | 2010.04.17 |