Java

ClassNotFoundException 와 NoClassDefFoundError 차이

[하마] 이승현 (wowlsh93@gmail.com) 2015. 7. 31. 17:41

ClassNotFoundException :   

클래스 로더가 클래스 패스에서 해당 클래스를 못 찾으면 발생한다. 이 에러가 발생하면 기본적으로 클래스 패스와 
그 패스에 해당 클래스가 존재하는지 확인해야한다.

NoClassDefFoundError :

이것이 발생하면 이유를 찾기가 꽤나 골치아퍼 지는데, 이것은 컴파일타임때 요구되는 클래스가 존재하지만 
런타임때 클래스들이 바뀌거나, 제거되거나, 클래스의 스태틱 초기화가 예외를 던지면 이것이 발생한다. 
먼 소리냐하면 클래스패스에 클래스가 존재하더라도, 이 클래스에 요구되는 클래스들중 하나에 문제가 생겼다는 
이야기이다. 따라서 이 클래스와 의존관계를 맺는 모든것들을 살펴봐야한다.

예제 :

public class Test1
{
}
public class Test 

{
   public static void main(String[] args)

   {
        Test1 = new Test1();    
    }

}

     
두개의 (Test, Test1)  클래스를 컴파일한후에 , Test1.class 파일을 지우고 RUN 해보면 아래의 예외를 던진다.

Exception in thread "main" java.lang.NoClassDefFoundError: Test
        at Test1.main(Test1.java:5)
Caused by: java.lang.ClassNotFoundException: Test
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 1 more
 

번외 :  JAVA 와 WAS 의 클래스 로딩 차이 

Java에서 클래스로더가 클래스나 리소스를 찾을 땐 부모 클래스로더에게 우선 이를 위임하게 된다. 그래서 부모 클래스로더가 해당 클래스나 리소스를 찾을 수 없게되면 자신이 직접 해당 클래스나 리소스를 찾는 형태로 동작하게 된다. 아래는 이와 관려련하여 Java 7의 API문서에서 발췌한 내용이다.

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself.

클래스로더가 복수개 존재할 때 공통으로 사용되는 클래스나 리소스들을 모두 각자 로딩한다면 해당 자원들을 중복되게 로딩함으로써 JVM의 permanent generation를 낭비하게 된다. 하지만 부모 클래스로더에서 해당 자원들을 먼저 찾는 방식으로 동작할 경우 복수개 클래스로더의 부모가 모두 같을 경우 부모에 로드된 자원을 함께 사용하게 되므로 자원의 낭비를 막을 수 있다. 이러한 방식의 클래스로딩 메커니즘을 parent-first / child-last 방식이라고 명명하도록 하겠다.

하지만 WAS에서는 parent-first / child-last 방식을 사용하지 않는다.

(이미지 출처: Tomcat 클래스로더 How-to)

대표적은 WAS인 Tomcat의 클래스로더 계층구조는 위의 그림과 같다. 만약 WAS에서 Java와 같은 parent-first / child-last 방식의 클래스로딩 메커니즘을 사용할 경우 각각의 웹앱들은 Common 클래스로더에서 로딩된 클래스를 함께 사용하게 된다. 즉, WAS의 lib 폴더에 존재하는 클래스들을 모든 웹앱에서 함께 사용하게 되어버린다.

웹앱 개발 시에 A라는 라이브러리의 최신 버전인 2.0 버전을 사용했다고 하자. 개발과 검증을 모두 완료하고 해당 웹앱을 WAS에 배포했는데 만약 Common 클래스로더에 이미 A라는 라이브러리의 1.0 버전이 로딩되어 있다면 어떻게 될 것인가? 1.0과 2.0의 클래스 명(패키지 경로 포함)이 같고 기능이 바뀌었다면 2.0기반에서 작업된 웹앱은 제대로 동작하지 않을 것이다. 이와 같은 문제가 발생할 수 있기 때문에 WAS에서는 parent-first / child-last 방식의 클래스로딩 메커니즘이 아닌 parent-last / child-first 방식의 클래스로딩 메커니즘을 사용한다.

참고로 Tomcat에서는 아래와 같은 순서로 클래스로더를 사용한다. (엄밀히 말하면 parent-last / child-first가 아니라 Common과 웹앱만 순서가 바뀐 것이다.)

  • Bootstrap
  • System
  • /WEB-INF/classes
  • /WEB-INF/lib/*.jar
  • Common


2014.08.12 추가

Tomcat이 아닌 다른 WAS들의 경우 모두 parent-last / child-first 방식을 디폴트로 따르는 것은 아닌 것 같다. 하지만 그런 WAS들의 경우 어떤 클래스로딩 메커니즘 방식을 사용할지 설정할 수 있다. 자체 서비스를 하는 기업의 경우 이런 설정을 자신들이 변경할 수 있지만 WAR 패키지로 판매하는 제품을 만드는 기업의 경우 WAS의 클래스로딩 메커니즘 설정에 따라 충돌이 발생할 수 있기 때문에 주의해야 한다.

http://epicdevs.com/16

레퍼런스 : http://www.javaroots.com/2013/02/classnotfoundexception-vs.html