Enemy of the redeployment (2)

엔터프라이즈 자바 2007. 1. 3. 20:07 posted by 엔트웍스
Enemy of the redeployment

두번째, 클래스로더ClassLoader를 이해하자.

퀴즈.
JDK1.3(혹은 그 이상)에서 ClassLoader 인스턴스를 얻는 방법을 나열하시오.

답:

지난 회에서도 언급했지만, 클래스로더는 정말 중요하므로 아키텍트라면 반드시 이해해야 한다.
우선 클래스로더의 JavaDoc부터 살펴보자.

API보다 중요한 것은 다음에 기술할 클래스로더의 작동 규칙이다. 다음 중 이탤릭체 부분은 Ted Newared의 Effective Enterprise Java 책의 Item 70에서 설명한 규칙을 원문을 그대로 번역한 것이다.

규칙1 - 클래스로더는 계층구조를 형성한다(그림1 참조). 이를 보다 상세하게 설명하자면, 클래스로더의 각 인스턴스는 한 개(혹은 null)의 부모 클래스로더 인스턴스를 참조한다. 자식 인스턴스에 대한 참조는 가지지 않는다.
JVM은 기본적으로 세개의 클래스로더 인스턴스를 생성한다.
우선 자바의 가장 기본적인 클래스(java.lang.Object 같은)를 로딩하는데 사용하는 부트스트랩bootstrap 클래스로더 인스턴스를 생성한다.
두 번째로, jre/ext/lib 디렉토리 밑에 jar로 패키징된 클래스를 로딩하기 위한 확장extension 클래스로더이다.
세 번째로, CLASSPATH로 지정된 클래스를 로딩하기 위한 클래스로더이다. 단순한 애플리케이션이라면 개발한 클래스는 모두 이 클래스로더에 의해 로딩될 것이다.
이러한 계층구조는 위임모델delegation model이라 불리는 클래스로더의 작동 모델을 위한 것이다. 위임모델을 설명하면 다음과 같다.
클래스로더는 자신에게 로딩되지 않은 클래스를 로딩하도록 요청받으면, 우선 자신의 부모 클래스로더에게 요청한다. 부모의클래스로더에 없는 경우에만 자신이 로딩한다. 즉 어느 클래스로더 이든지 한번 로딩된 클래스라면, 이 클래스로더의 자식클래스로더는 로딩하지 않는다. (이는 우리가 흔히 static 키워드를 이용해서 구현하는 싱글턴singleton은 VM 수준의싱글턴이 아니라, 클래스로더 계층에 대한 싱글턴이라는 사실을 알려준다.) 그리고 마지막으로, 부모 클래스로더는 자식 클래스로더에 대해 아는 바가 전혀 없다.


  (그림1 - 클래스로더 계층구조)

화살표는 상속이 아니라 인스턴스의 부모/자식 관계를 나타낸다.
http://www.objectsource.com/j2eechapters/Ch21-ClassLoaders_and_J2EE.htm 에서 발췌


규칙2 - 클래스는 필요 시점에 로딩된다loaded lazily. 이는 옵션에 따라 다르지만, 대부분의 VM은 반드시 초기에 필요한 클래스를 제외한 나머지 클래스 들은 모두 실행 중 필요할 때 로딩한다. VM에 따라 옵션을 주면 선로딩eager loading도 가능한데, lazy vs. eager에 따라 애플리케이션의 실행 방식이 달라지는 일은 없도록 하는 것이 일반적이다(즉 NoClassDefFoundError가 발생하는 시점 등은 lazy나 eager나 동일).

규칙3 - 클래스 A를 로딩 할 때는, 클래스 A를 필요로 하게 한 클래스 B를 로딩한 클래스로더 인스턴스가 로딩을 담당한다. ClassLoader API를 직접 사용하는 경우는 예외이다.

규칙4 - 클래스의 유일성은 클래스명, 패키지명, 그리고 이 클래스를 로딩한 클래스로더 인스턴스의 조합에 의해 식별된다. 일반적으로 알고 있는 바와 같이 클래스명과 패키지명은 조합의 일부일 뿐이다. 이 규칙에 유념하지 않는다면 이전 글에 설명한 바와 같이 ClassCaseException을 피할 수 없다.

아래의 규칙은 내가 임의로 추가한 것이다.
규칙6 - ClassLoader API 문서에서 볼 수 있듯이 클래스로더 API에는 언로드unload가 없다. 이에 대한 근거는 Java Language Specification의 이 부분에 자세하게 나와 있다. 클래스로더 인스턴스는 그냥 둔채 클래스를 리로드reload 하기 위해, 이런 규칙을 깨는 클래스로더를 구현하는 것이 가능하기는 하다. 그러나 이는 시스템 전체의 신뢰성을 심각하게 손상시킬 수 있다.

규칙7 - 클래스는 클래스로더 인스턴스 자체가 GC될 때에는 같이 언로드 된다. 그런데 JavaEE 서버에서 실행되는 애플리케이션이라면, 클래스로더 인스턴스 자체는 보통 제어할 수 없는 것이 일반적(게다가 GC 조차도 VM 마음이다)이므로 이 사실에 맹목적으로 의존하기는 힘들다.

일반적으로 이러한 규칙은 정확하게 몰라도 별 문제를 일으키지 않는다. 그러나 JavaEE 환경이라면 클래스로더를 이해하지 못하면 redeployment와 관련하여 다음과 같은 문제가 발생한다.

1. redeploy 후에 ClassCastException 발생 문제. 이전 글에서 이야기했듯이 클래스로더 인스턴스의 생명주기 보다 긴 객체(예를 들어 HttpSession에 저장되는 객체)의 경우 이런 문제가 발생한다.
2. redeploy를 어떤 단위로 할 것인가의 문제. 하나의 EJB만 redeploy할 수 있는가?

첫 번째 ClassCastException에 대한 문제에 대한 해결 방법은 Ted Neward에 의하면 세 가지가 존재한다.
  1. 해당 클래스에 대한 인터페이스(혹은 상위 클래스)를 정의하고 이를 리로드를 위한 클래스로더보다 상위계층에서 로딩되도록 만들고, 이 객체에 대한 참조는 인터페이스로만 참조한다. 이는 예를 들어 HttpSession에 저장되는 객체라면 모두 인터페이스와 구현 클래스를 구분해서 구현하고, 따로 deploy하는 수고를 해야 한다.
  2. 이 클래스 대신 HashMap이나 String 같이 상위계층에서 로딩되는 클래스를 사용한다. 개인적으로는 가장 효율적이면서도 단순한 방법이라고 생각한다.
  3. 직렬화 시킨 인스턴스를 사용한다. 성능 문제와 더불어 HttpSession의 경우 JSP의 session 키워드를 사용할 수 없게 되는 문제가 있다.

두번째 redeploy의 단위 문제는 다음 글의 주제가 될 것이다.
  1. Commented by Favicon of http://cbiscuit.info BlogIcon cbiscuit at 2007.01.04 20:18

    ClassLoader가 사람잡는다...

  2. Commented by Favicon of http://younghoe.info/ BlogIcon 영회 at 2007.01.12 16:41

    저.. CBD 바닥 떠납니다. ^^

  3. Commented by Favicon of http://greenfrog7.egloos.com BlogIcon greenfrog at 2008.10.11 08:31

    좋은 정보 잘 보고 갑니다. 퍼가요 ~