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



이 부분을 어떻게 풀었는지 확인했다.

--- 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이 안되도록 했다.
병렬적으로 클래스로딩이 될 때, 문제가 꼬일 수 있는 부분이 패치한 것으로 보인다.







Posted by '김용환'
,