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 를통해적절히배치하는일련의작업을수행하는모듈이라고정의내릴수있다. Class Loader 에서 Class 를언제 Load 가어느시점에수행되느냐에따라 Dynamic Loading 과 Runtime Dynamic Loading 으로구분된다. Load Time Dynamic Loading 하나의 Class 를 Loading 하는과정에서이와관련된 Class 들을한꺼번에 Loading 한다. 아래의소스코드 1 을보면 Hello 라는 Class 에서 String 객체를 main() Method Parameter 로사용하고있고 main() Method 내부에서는 System 객체를호출하고있다. 이경우 Hello Class 가 Class Loader 에의해 JVM 내로 Loading 될때 java.lang.string Class 와 java.lang.system Class 가동시에 Loading 이이루어진다. Part 2 APM 351
Public class Hello { Public static void main(string[] args) { System.out.println( Hello Java ); [ 소스코드 1] Load Time Dynamic Loading 방식 Runtime Dynamic 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 를각각하나씩가지고있어자신이 Load 한 Class 의 Full Qualified Name 을저장해놓는다. 352 2013 기술백서 White Paper
[ 그림 1] Namespace 와 Class 의 Load [ 그림 2] Referenced Object 의 Load [ 그림 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) 하는일련의방법을말한다. Part 2 APM 353
[ 그림 3] Class Loader 의위계구조 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 하는작업을수행한다. Application Class Loader $Classpath 또는 java.class.path 에위치한 Class 들을 Load 한다. 354 2013 기술백서 White Paper
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 Loader 이며 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 로구성한다. Part 2 APM 355
- 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 Loader 관련 Options Class Sharing 을사용하는경우 File 형태의 Class 또는 jar 파일을 Memory Mapped File(Shared Archive) 형태로떨궈놓는다. Shared Archive File 은 jar 대신 jsa 라는확장자를사용한다. 356 2013 기술백서 White Paper
-Xbootclasspath:<path(s) or file(s)> : Bootstrap Class Loader 의검색경로또는 Bootstrap Class Loader 로 Load 할파일을설정한다. Hotspot JVM 과 IBM JVM 모두동일하게사용한다. -Xbootclasspath/a:<path(s) or file(s)> : 경로나파일을 Boot Classpath 의뒤에추가한다. Class Loader 는파일시스템의경로나파일의탐색을 Boot Classpath 에기술한순서대로수행한다. 그러므로이옵션은 Java API 다음에읽어들일경로나파일을기술할때사용이가능하다. -Xbootclasspath/p:<path(s) or file(s)> : 경로나파일을 Boot Classpath 의앞에추가한다. Class Loader 는파일시스템의경로나파일의탐색을 Boot Classpath 에기술한순서대로수행한다. 그러므로이옵션은 Java API 보다먼저읽어들일경로나파일을기술할때사용한다. Hotspot JVM 과 IBM JVM 모두동일하게사용이가능하다. -cp, -classpath<path(s) or file(s)> : System 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() 검색으로클래스위치를찾을수있다. Part 2 APM 357
Class 관련 Exception ClassNotFoundException과 NoClassDefFoundError ClassNotFoundException 과 NoClassDefFoundError 는둘다클래스를찾지못해서발생하는에러이다. 그렇다면어느경우에 ClassNotFoundException 을발생시키고어느경우에 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(); 358 2013 기술백서 White Paper
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 클래스가존재하지않았다면이는 ClassNotFoundException 을던졌을것이다. Part 2 APM 359
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 관련내용에대한이해를필요로한다. 이러한부분에대한이해를넓힌다면빠르게원인파악및분석을할수있을것이다. 360 2013 기술백서 White Paper