대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 Class Loader
등록일 조회수 5049
첨부파일  

Class Loader

㈜엑셈 컨설팅본부 /APM팀 김 다운



개요

클래스 loader 는 자바의 기능 중 하나로써 런타임에 클래스 파일을 찾고 로딩하는 임무를 맡는 다 . WAS 마다 Class Loading 의 방식에 조금씩 차이가 있으며 , Intermax 설치 시에 classpath 옵션을 WAS 에 추가 함으로서 Class loading 을 한다 . 이에 클래스 loader 의 로딩 메커니즘을 이해하고 WAS 기동시 발생하는 클래스 관련 에러에 대해 알아보고자 한다 . 좀 더 세부적으로 나아가 클래스 로딩 메커니즘을 이해 함으로서 application 의 ClassNotFound 나 ClassCastException 에러를 디버깅할 때 도움을 주고자 한다 .


Class Loader Loading 시점

JVM 은 Class Loader 를 이용해 필 요한 Class 를 Loading 한다 . 이 때 , Class 가 참조되는 순간 동적으로 Load 및 Link 가 이루어지는 Dynamic Loading 을 담당하는 주체가 Class Loader 이 다 . 즉 JVM 내로 Class 를 Load 하고 Link 를 통해 적절히 배치하는 일련의 작업을 수행하는 모 듈이라고 정의 내릴 수 있다 . C lass Loader 에서 Class 를 언제 Load 가 어느 시점에 수행되느 냐에 따라 Dynamic Loading 과 Runtime Dyn amic Loading 으로 구분된다 .


Load Time Dynamic Loading

하나의 Class 를 Loading 하는 과정에서 이와 관련된 Class 들을 한꺼번에 Loading 한다 . 아래 의 소스 코드 1 을 보면 Hello 라는 Class 에서 String 객체를 main() Method Parameter 로 사 용하고 있고 main() Method 내부에서는 System 객체를 호출하고 있다 . 이 경우 Hello Class 가 Class Loader 에 의해 J VM 내로 Loading 될 때 java.lang.String Class 와 java.lang.System Class 가 동시에 Loading 이 이루어진다 .


Public class Hello { Public static void main(String[] args) { System.out.println(“Hello Java”); } }

[ 소스 코드 1] Load Time Dynamic Loading 방식


Runtime Dy namic Loading

객체를 참조하는 순간에 동적으로 Loading 하는 방식이다 . 아래의 소스코드 2 를 보면 Hello Class 에서 어떤 Class 를 Load 해야 할 지는 인수로 넘어온 이후에나 알 수 있다 . 즉 Class.forName(args[0]) 를 호출하는 순간에 args[0] 에 해당하는 Class 을 Loading 할 수 밖에 없는 상황이 된다 .


Public class Hello { Public static void main(String[] args) { Class c1 = Class.forName(args[0]); } }

[소스 코드2] Runtime Dynamic Loading 방식


Class Loader 의 Class 확인 방법

Class Loader 는 JVM 으로 Class 를 Load 하고 배치하는 일을 담당한다 . 이 때 , JVM 은 동일한 Class 를 중복해서 Loading 하지 않기 위해 Class Loader 별로 Namespace 라는 것을 사용하 여 Full Qualified Name(PackageName 과 ClassName 을 점 (.) 으로 연결한 이름 ) 을 저장해 놓는다 . JVM 에서는 Class Loader Name + Package Name + Class Name 까지 동일해야 같 은 Class 라고 인식한다 . Class Loader 가 Class 를 Load 할 때 JVM 에 있는 모든 Class 이름을 확인하는 것이 아니라 Class Loader 가 Namespace 를 각각 하나씩 가지고 있어 자신이 L oad 한 Class 의 Full Qualified Name 을 저장해 놓는다 .


[ 그림 1] 을 보면 package.className 이라는 Class 를 Load 하려는데 , Class Loader 1 에는 이미 같은 이름의 Class 가 Load 되어 있기에 해당 Class 를 Load 하지 않는다 . 하지만 Class Loader 2 의 Namespace 에는 같은 이름을 가진 Class 가 없기 때문에 Load 가 가능하다 . 여기 서 주의할 점은 같은 JVM 내에서 같 은 Class 라도 Class Loader 만 다르다면 중복해서 Load 가 가능하다는 것을 의미한다 .

[ 그림 2] 는 Class Loader 2 가 Symbolic Reference 를 수행할 때이다 . Class Loader 는 Symbolic Reference 를 수행할 때 자신의 Namespace 를 검색한다 . 그런데 Java 는 자신이 참 조하는 Class 를 Load 할 때는 반드시 참조하는 Class 와 참조되는 Class 가 동일한 Class Loader 를 사용해야만 한다는 규칙이 있다 . [ 그림 2] 에서 exem.package.jvmClass 를 Load 하 는 Hello Class 를 수행하기 위해 Class Loader2 는 Class Loader1 에 있는 exem.package.jvmClass Class 를 Load 하여 동일한 Class Loader 에 위치하도록 한다 . 이는 참조관계가 있는 Class 들은 같은 Class Loader 를 사용해야 한다는 원칙 때문에 발생한 일이다 .


Class Loader 의 호출 관계

JVM 내에는 여러 개의 Class Loader 가 있으며 이들 Class Loader 는 위계 구조 , 계층 구조를 가지고 있다 . Class Loader 는 이러한 위계 구조를 바탕으로 서로에게 임무를 위임 (Delegation) 하는 일련의 방법을 말한다 .


Bootstrap Class Loader

JVM 기동 시에 가장 먼저 생성되며 $JAVA_HOME/jre/lib/rt.jar 를 Load 하는 작업을 수행한다 . 그 후 Object Class 를 포함한 Java API 들을 Load 하게 된다 . Bootstrap Class Loader 는 부모를 가지지 않는 가장 상위의 Class Loader 로서 다른 Class Loader 와 달리 Java 가 아닌 Native Code 로 구현되어 있다 .


Extension Class Loader

Bootstrap Class Loader 를 부모로 하고 기본 Java API 를 제외한 $JAVA_HOME/jre/lib/ext 에 위치한 확장 Class 들을 Load 하는 작업을 수행한다 .


Applic ation Class Loader

$Classpath 또는 java.class.path 에 위치한 Class 들을 Load 한다 .


User Defined Class Loader

Application 에서 직접 생성이 가능하며 , 보통 WAS 나 Framework 에서는 User Defined Class Loader 를 생성해서 사용하는 경우가 많다 . System Class Loader 의 하위에 있으며 User Defined Class Loader 들끼리도 계층을 가지게 된다 .


Class Loading

Class Loader 는 Class 를 Load 할 때 자신의 Namespace 를 검색하여 이미 Load 된 Class 라면 이를 반환하고 , Load 되어 있지 않았다면 Parent 위임을 하게 된다 . Parent Class Loader 가 이를 완료하지 못하면 자신이 Class 를 Load 한다 .


Class Loader Tree in WAS

JVM 에서 Default 로 사용하는 Class Loader 는 System Class Load er 이며 User Defined Class Loader 를 생성할 때 별도의 Parent 를 지정하지 않게 되면 System Class Loader 가 부모가 된다 . WAS 의 경우는 일반적으로 User Defined Class Loader 를 만들어 이를 주로 사용하게 된다 . 이들은 $WAS_HOME 와 같은 특정 directory 를 설정하여 검색하도록 되어 있다 . EJB 모듈과 WAR 모듈을 Deploy 하기 위한 Class Loader 를 구성하고 이들 또한 계층 구조를 확장한 Tree 구조를 가지고 있기 때문에 Class Loader Tree 라고 표현한다 .

. Application(EAR 또는 Deploy 된 EJB 나 WEB 모듈 ) 들 끼리 섞이지 않도록 독립적으로 구성 되어야 한다 .
. Application 내의 모듈 사이에는 Class 를 공유한다 .
. 다수의 Class Loader 로 구성한다 .

- Note
- WAR(Web Archive) : J2EE에서 Web Application를 JAR형태로 묶은 것을 의미한다. 다시 말해 WebRoot 하위의 jsp 파일 또는 Servlet 그리고 /WEB-INF/classes/* 등의 파일과 Web Application의 Deployment Descriptor 인 /WEB-INF/web.xml을 묶은 것이다.
- JAR(Java Archive) : J2EE에서는 보통 EJB 모듈을 묶어놓은 것을 의미한다. EJB 모듈은 EJB 컴포넌트 및 Bean들 그리고 Deployment Descriptor를 포함한다.
- EAR(Enterprise Application) : Enterprise Application을 의미하며 EJB모듈인 JAR와 WEB 모듈인 WAR를 /Meta-INF/application.xml과 함께 묶어 놓은 것을 말한다.


JVM Process 의 Class 공유 기능

JVM 의 각 프로세스들 사이에서 Load 된 Class 를 공유하는 기능으로 원래의 의도는 JVM 이 rt.jar 와 같은 기본 Class 파일들을 Load 및 공유해 놓으면 이를 이용하여 다른 JVM 의 기 동시간을 줄이기 위한 것이다 . Class Sharing 을 사용하게 되면 Class Loading 시 File System 탐색 전에 Shared Class 를 먼저 탐색한다 .


Hotspot JVM

Class Sharing 을 사용하는 경우 File 형태의 Class 또는 jar 파일을 Memory Mapped File(Shared Archive) 형태로 떨궈 놓는다 . Shared Archive File 은 jar 대신 jsa 라는 확장자를 사용한다 .

Class L oader 관련 Options

Class Sharing 을 사용하는 경우 File 형태의 Class 또는 jar 파일을 Memory Mapped File(Shared Archive) 형태로 떨궈 놓는다 . Shared Archive File 은 jar 대신 jsa 라는 확장자를 사용한다 .

. - Xbootclasspath: : Bootstrap Class Loader 의 검색 경로 또는 Bootstrap Class Loader 로 Loa d 할 파일을 설정한다 . Hotspot JVM 과 IBM JVM 모두 동일하게 사용 한다 .

. - Xbootclasspath/a: : 경로나 파일을 Boot Classpath 의 뒤에 추가한다 . Class Loader 는 파일시스템의 경로나 파일의 탐색을 Boot Classpath 에 기술한 순서대로 수행한다 . 그러므로 이 옵션은 Java API 다음에 읽어 들일 경로나 파일을 기술할 때 사용이 가능하다 .

. - Xbootclasspath /p: : 경로나 파일을 Boot Classpath 의 앞에 추가한다 . Class Loader 는 파일시스템의 경로나 파일의 탐색을 Boot Classpath 에 기술한 순서대로 수행한다 . 그러므로 이 옵션은 Java API 보다 먼저 읽어 들일 경로나 파일을 기술할 때 사 용한다 . Hotspot JVM 과 IBM JVM 모두 동일하게 사용이 가능하다 .

. - cp, - classpath : Sys tem Class Loader 의 검색 경로 또는 System Class Loader 로 Load 할 파일을 설정하는 옵션이다 . Class Loader 의 경로를 설정하는 옵션 가운 데 가장 많이 사용하는 옵션인 만큼 설정하는 방법도 다양하다 . Hotspot JVM 과 IBM JVM 모두 동일하게 사용이 가능하며 이 옵션 대신 CLASSPATH 라는 환경변수를 사용해도 동일 하게 설정된다 .


Class Loader 고려 사항

Java.lang.ClassLoader 클래스의 resolveClass 및 defineClass 메서드는 지원되지 않으므로 이러한 메서드를 사용하지 말아야 한다 . 기존의 Java 언어 응용 프로그램에서 loadClass() Method 를 다시 작성하여 .NET Framework Class.forName() 의 버전 하나를 호출한 다음 응용 프로그램 구성 파일 (.config) 에 적절한 엔트리를 만들어 CLR 에서 응용 프로그램에 대한 관리되는 어셈블리를 찾아 load 할 수 있도록 한다 . 관리되는 어셈 블리가 응용 프로그램의 작업 디렉터리에 있는 경우에는 구성 파일을 사용할 필요 없이 Class.forName() 검색으로 클래스 위치를 찾을 수 있다 .


Class 관련 Exception

ClassNotFoundException 과 NoClassDefFoundError

ClassNotFoundException 과 NoClassDefFoundError 는 둘 다 클래스를 찾지 못해서 발생하는 에러이다 . 그렇다면 어느 경우에 ClassNotFoundExcepti on 을 발생시키고 어느 경우에 NoClassDefFoundError 를 발생시키는지 알아보도록 하자 . ClassNotFoundException 는 A 라 는 클래스를 클래스 loader 가 로딩하려고 할 때 찾을 수 없는 경우에 발생한다 . NoClassDefFoundError 는 클래스 loader 가 A 라는 클래스를 로딩하는 도중 B 라는 클래스를 내재적으로 로딩을 하다가 B 클래스를 찾을 수 없는 경우에 발생한다 . 즉 A 클래스 내부에 정의 된 B 클래스가 클래스 패 스 어딘가에 존재하지 않아서 , A 클래스를 로딩 하다가 A 클래스에 정 의된 B 클래스를 찾지 못하는 것이다 . 아래의 예제를 보도록 하자 .


Delegate.java public class Delegate { public void delegate(){ AppScopeTest test = new AppScopeTest(); test.print(); } } AppScopeTest.java import com.eint.sample2.NoClassTest; public class AppScopeTest { public void print(){ NoClassTest test = new NoClassTest(); test.printout(); } } NoClassTest.java package com.eint.sample2; public class NoClassTest { public void printout(){ System.out.println("*************** NoClassTest.printout() -1"); } }

Delegate 클래스에서 내부적으로 AppScopeTest 란 클래스를 생성하고 있다 . AppScopeTest 를 살펴보면 안에 NoClassTest 이란 클래스를 정의하여 사용하고 있다 . 컴파일 된 클래스를 WAS 에 설치 한 후 NoClassTest 클래스를 삭제한다 . 임의로 만든 test.jsp 에서 Delegate 클래 스의 delegate Method 를 호출하면 아래와 같은 오류가 떨어진다 .


java.lang.NoClassDefFoundError: com.eint.sample2.NoClassTest at com.eint.sample.AppScopeTest.print(AppScopeTest.java:10) at com.eint.Delegate.delegate(Delegate.java:17) at com.ibm._jsp._test._jspService(_test.java:70)

NoClassTest 클래스가 컴파일 시에는 존재 했으나 실제 Delegate 가 로딩이 되면서 AppScopeTest 를 내재적으로 로딩하는 과정 속에서 AppScopeTest 안에 정의되어 사용되는 NoClassTest 가 클래스 패스 안에 존재하지 않아 NoClassDefFoundError 를 발생시키게 된다 . 만일 위의 소스에서 AppScopeTest 클래스가 존재하지 않았다면 이는 ClassNotFoundExcep tion 을 던졌을 것이다 .


ClassCastException

동일한 JVM 상에서 동일한 이름을 갖는 서로 다른 컴포넌트가 두 번 메모리에 load 되는 경우에 발생하는 것이 일반적이다 . 예를 들면 기존에 메모리에 load 된 PoolManagerBean 이 컨테이너 상에 있다고 하면 PoolManagerBean 클래스를 수정하고 재 compile 하여 다시 메모리에 load 되는 경우에 ClassCastException 현상이 발생할 수 있다 . 이러한 경우 컨테이너 를 restart 하면 해결 가능하다 . ClassCastException 이 발생하면 Classpath 를 확인해서 현재 지정된 Classpath directory 에 두 군데 이상의 동일한 이름의 클래스가 포함되어 있는지 확인해 보도 록 한다 . 동일한 클래스 파일인 경우에는 문제가 되지 않지만 , 동일한 이름의 클래스 파일이 서 로 다른 경우에 문제가 된다 . 앞에서 말했듯이 동일한 JVM 상에서 서로 다른 컴포넌트가 두 번 메모리에 load 되는 경우이므로 에러가 발생하게 된다 .


결론

지금까지 Class Loader 에 대해 알아보았다 . Class Loader 를 통해 Class 를 JVM 내로 배치하 는데 , Class Loader 의 Loading 방식과 호출 관계를 이해함으로써 Class loader 의 동작 방식에 대해 이해할 수 있었다 . Class Loader 의 동작 방식과 더불어 Class Loader 관련 Option 및 Exception 에 대해서도 알아보았다 . 지원을 하면서 Exception 발생 시 직접 코드 내 부를 확인하 여 원인을 찾는 일은 드물지만 , 종종 발생하는 Exception 과 WAS 설정 시 볼 수 있는 Option 들 에 대해 이해함으로써 , 접했을 시 좀 더 쉽게 원인을 파악하고 내용을 이해할 수 있을 것이다 . 여 기서는 Class Load 관련해서 알아보았다 . 하지만 실제로는 GC, Java Heap Memory 등 다양한 Java 관련 내용에 대한 이해를 필요로 한다 . 이러한 부분에 대한 이해를 넓힌다면 빠르게 원인 파악 및 분석을 할 수 있을 것이다 .



출처 : (주)엑셈

제공 : DB포탈사이트 DBguide.net