Tomcat 클래스 로더에 대한 패치가 6.0.28에 이루어졌다. 클래스 로딩이 갑자기 많아져서, 중복된 클래스를 찾는 부분이 일어나 링크에러가 나는 경우이다. 이것 때문에 deadlock이 발생하는 케이스이다. (동기화 문제는 늘 어려운 문제이다. )

이 문제는 클래스로딩이 많은 웹 서비스에서 어쩌다 한번 나타날 수 있으며 그 파장은 deadlock이다. WebappClassLoader 클래스의 loadClass 메서드와 findClass 안에서 로딩한 클래스가 두 개가 되면서 나타나는 현상이다.

이에 대한 톰캣 패치 내역과 소스를 살펴본다.

http://tomcat.apache.org/tomcat-6.0-doc/changelog.html

44041: Fix threading issue in WebappClassLoader that can lead to duplicate class definition under high load. (markt/fhanik)

 

https://issues.apache.org/bugzilla/show_bug.cgi?id=44041

Class clazz = Thread.currentThread().getContextClassLoader().loadClass("<Class Name>"); java.lang.LinkageError: duplicate class definition: com/ubikingenierie/bug/ClassLoadedDynamically at java.lang.ClassLoader.defineClass1(Native Method)

 

 

http://svn.apache.org/viewvc?view=revision&revision=941868

기존 코드 상으로는 클래스 로딩시 클래스의 이름을 가지고 했다. 이는 쓰레드 경쟁 관계에서 똑같은 클래스를 읽으므로서,

 

1. jsp 클래스 로딩시 synchronized가 새로 추가되었다.

/tomcat/tc6.0.x/trunk/java/org/apache/jasper/servlet/JasperLoader.java

+ public synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {

 

2.  컴파일러가 로드된 클래스를 최적화되지 않도록 한다.

tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/ResourceEntry.java

+ public volatile Class loadedClass = null;

 

3. 톰캣 디폴트 클래스 로더인 WebappClassLoader 클래스에서 클래스 이름에 대한 synchronized가 아닌 메소드 단위의 synchronized로 처리함으로서, 속도보다는 안정성을 더 중점을 둘 수 있다.

 

tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java

public Class loadClass(String name, boolean resolve)  {

  synchronized (name.intern()) {

…..

}

 

=>

public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {

}

 

 

protected Class findClassInternal(String name) {

    synchronized (name.intern()) {

       clazz = entry.loadedClass;

        if (clazz != null)

                return clazz;

…..

}

 

protected Class findClassInternal(String name) {

    synchronized (this) {

       clazz = entry.loadedClass;

        if (clazz != null)

                return clazz;

…..

}

Posted by '김용환'
,