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


