클래스 로딩 프로세스
클래스 로더의 주요 작업은 클래스 파일을 JVM에로드하는 것입니다. 아래 그림과 같이 프로세스는 세 단계로 나뉩니다.
1.로드 :로드 할 클래스 파일을 찾아 바이트 스트림을 JVM에로드합니다.
2. 링크 : 속성, 메소드 및 참조 클래스와 같은 정보를 저장하기 위해로드하도록 가장 기본적인 메모리 구조를 할당하십시오. 이 단계에서 수업은 여전히 사용할 수 없습니다.
(1) 확인 : 형식 및 보안과 같은로드 된 바이트 스트림을 확인하십시오.
(2) 메모리 할당 :이 클래스가 속성, 방법 및 참조 클래스를 나타내도록 메모리 공간을 준비합니다.
(3) 분석 : 부모 클래스, 구현 된 인터페이스 등과 같은이 클래스에서 참조 된 기타 클래스로드.
3. 초기화 : 클래스 변수에 값을 할당합니다.
클래스 로더 레벨
아래 그림의 점선은 JDK가 제공하는 몇 가지 중요한 클래스 로더이며 자세한 설명은 다음과 같습니다.
(1) 부트 스트랩 클래스 로더 : 기본 기능이 포함 된 클래스를 시작할 때 JAVA_HOME/LIB 디렉토리 또는 -xbootclasspath 지정된 디렉토리에 JAR 패키지를로드하십시오.
(2) Extention Class Loader : Java_home/Lib/Ext Directory 또는 -djava.ext.dirs 지정된 디렉토리에 JAR 패키지를로드하십시오.
(3) 시스템 클래스 로더 : 지정된 디렉토리의로드 클래스 경로 또는 -djava.class.path 클래스 또는 JAR 패키지.
당신이 알아야 할 것은 다음과 같습니다.
1. 부트 스트랩 클래스 로더 외에도 다른 클래스 로더는 java.lang.classloader 클래스의 서브 클래스입니다.
2. 부트 스트랩 클래스 로더는 Java에서 구현되지 않았습니다. 개인화 된 클래스 로더를 사용하지 않으면 java.lang.string.class.getClassLoader ()가 NULL이고 Extension Class 로더의 상위 로더도 NULL입니다.
3. 클래스 로더를 얻는 몇 가지 방법 :
(1) 부트 스트랩 클래스 로더 획득 : 부트 스트랩 클래스 로더를 얻으려고 할 때 얻을 수있는 것은 무효가되어야합니다. 다음과 같은 방법으로 확인할 수 있습니다. java.lang.string.class.getclassloader ()와 같은 Rt.jar 패키지의 클래스 객체의 getClassLoader 메소드를 사용하여 확장 클래스 로더를 얻거나 얻은 다음 getparent 메소드를 호출하여 얻습니다.
(2) Extention Class 로더를 얻으십시오 : Java_home/Lib/ext 디렉토리 아래 JAR 패키지의 클래스 객체의 getClassLoader 메소드를 사용하거나 먼저 시스템 클래스 로더를 얻은 다음 GetParent 메소드를 통해이를 얻으십시오.
(3) 시스템 클래스 로더 획득 : 기본 함수가 포함 된 클래스 객체의 getClassLoader 메소드를 호출하거나 Thread.CurrentThread (). getContextClassLoader () 또는 ClassLoader.getSystemClassLoader () 주 기능 내에서;
(4) 사용자 정의 클래스 로더 얻기 : 클래스 객체의 getClassLoader 메소드를 호출하거나 recl.currentThread (). getContextClassLoader ();
클래스 로더의 운영 원리
1. 에이전트 원리
2. 가시성 원리
3. 독창성의 원리
4. 프록시 원칙 프록시 원칙은 클래스를로드 할 때 상위 로더 프록시를로드하도록 요청하는 클래스 로더를 말하며, 부모 로더는 아래 그림과 같이 부모 로더 프록시를로드하도록 요청합니다.
프록시 모드를 사용하는 이유는 무엇입니까? 우선, 클래스의 중복로드를 줄일 수 있습니다. (다른 이유가 있습니까?)
오해 할 곳 :
일반적으로 클래스 로더의 프록시 순서는 부모의 첫 번째라고 생각됩니다.
1. 클래스를로드 할 때 클래스 로더는 먼저 클래스를로드했는지 확인합니다. 그것이로드 된 경우, 반환됩니다. 그렇지 않으면 부모 로더에게 프록시를 요청하십시오.
2. 부모 로더는 부트 스트랩 클래스 로더까지 1의 작동을 반복합니다.
3. 부트 스트랩 클래스 로더에 클래스를로드하지 않으면 클래스를로드하려고 시도하고 성공하면 반환됩니다. 실패하면 ClassNotFoundException이 발생하면 하위 로더에 의해로드됩니다.
4. 서브 클래스 로더는 예외를 포착하여로드하려고합니다. 성공하면 돌아옵니다. 실패하면로드 된 서브 클래스 로더가 시작될 때까지 ClassNotFoundException을 던집니다.
이러한 이해는 부트 스트랩 클래스 로더, 확장 클래스 로더 및 시스템 클래스 로더와 같은 로더에게는 정확하지만 일부 개인화 된 로더는 그렇지 않습니다. 예를 들어, IBM Web Sphere Portal Server에서 구현 한 일부 클래스 로더는 부모의 마지막이며, 이는 먼저로드하려는 어린이 로더입니다. 부하가 실패하면 부모 로더가 요청됩니다. 그 이유는 다음과 같습니다. 모든 응용 프로그램에서 특정 버전의 log4J가 사용될 것으로 예상되면 WAS_HOME 라이브러리에 넣으면 시작 시점이로드됩니다. 응용 프로그램이 다른 버전의 Log4J를 사용하려는 경우 Log4J의 클래스가 이미 상위 로더에로드되었으므로 부모를 먼저 사용하는 경우 달성 할 수 없습니다. 그러나 부모가 마지막으로 사용되는 경우 응용 프로그램을로드하는 클래스 로더는 다른 버전의 LOG4J로드를 우선 순위로 삼습니다.
가시성 원리
클래스 로더에 대한 각 클래스의 가시성은 아래 그림과 같이 다릅니다.
지식을 확장하기 위해 OSGI는이 기능을 활용합니다. 각 번들은 별도의 클래스 로더로로드되므로 각 클래스 로더는 특정 클래스의 버전을로드 할 수 있으므로 전체 시스템이 여러 버전의 클래스를 사용할 수 있습니다.
독창성의 원리
각 클래스는 로더에서 최대 한 번에로드됩니다.
확장 된 지식 1 : 정확하게 말하면, 싱글 톤 패턴은 클래스 로더 세트에서 클래스의 한 사본 만 하나의 싱글 톤 객체를 나타냅니다.
확장 지식 2 : 클래스는 여러 클래스 로더로로드 할 수 있습니다. 각 클래스 객체는 자체 네임 스페이스에 있습니다. 인스턴스를 변환하는 클래스 객체 또는 유형을 비교할 때는 다음과 같은 동시에 해당 네임 스페이스를 비교합니다.
클래스 객체가 klassa라고 가정 할 때 Klass 클래스는 ClassLoadera에 의해로드됩니다. 클래스 객체가 klassb라고 가정하면 classloaderb에 의해로드되며 Klassa는 klassb와 같지 않습니다. 동시에 Classa 인스턴스가 KlassB로 캐스트되면 ClassScastException 예외가 발생합니다.
개인화 된 클래스 로더가 필요한 이유는 무엇입니까? 개인화 된 클래스 로더는 Java 언어에 많은 유연성을 추가합니다. 주요 용도는 다음과 같습니다.
1. 클래스는 네트워크, 데이터베이스의 여러 장소에서 또는 즉시 컴파일 된 소스 파일에서로드 할 수 있습니다.
2. 개인화 후 클래스 로더는 원칙적으로 런타임에 특정 버전의 클래스 파일을로드 할 수 있습니다.
3. 개인화 후 클래스 로더는 일부 클래스를 동적으로 내릴 수 있습니다.
4. 개인화 후 클래스 로더는 클래스를로드하기 전에 클래스를 해독하고 압축 할 수 있습니다.
클래스의 암시적이고 명백한 로딩
암시 적 부하 : 클래스가 참조, 상속 또는 인스턴스화되면 암시 적으로로드됩니다. 부하가 실패하면 noclassdeffounderror가 던져집니다.
명시 적 로딩 : 다음 방법을 사용하십시오. 부하가 실패하면 ClassNotFoundException이 발생합니다.
cl.loadclass (), cl은 클래스 로더의 인스턴스입니다.
class.forname (), 현재 클래스의 클래스 로더를 사용하여로드됩니다.
클래스의 정적 블록의 실행에 다음 클래스가있는 경우.
패키지 CN.FENGD; 공개 클래스 더미 {static {System.out.println ( "hi"); }} 다른 테스트 클래스 생성 :
패키지 CN.FENGD; public class classloaderTest {public static void main (string [] args)은 인스턴스티션 exception, 예외 {try { / * * 다른 로딩 방법을 던집니다. */ class c = classloaderTest.class.getClassLoader (). loadClass ( "CN.FENGD.DUMMY"); } catch (예외 e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); }}}실행 후 얼마나 효과적입니까?
Class.forname ( "Cn.Fengd.Dummy")으로 대체되면 어떨까요? 또는 새로운 더미 ()?
안녕하세요 출력이됩니다.
자주 묻는 질문 :
1. 지정된 유형이 다른 클래스 로더로로드 된 유형이 여전히 같은 유형입니까?
Java에서 클래스는 자격을 갖춘 클래스 이름을 식별자로 사용합니다. 여기에 언급 된 정확한 경기 클래스 이름에는 패키지 이름과 클래스 이름이 포함됩니다. 그러나 JVM에서 클래스는 전체 이름과 로딩 클래스 클래스 로더 인스턴스를 고유 식별자로 사용합니다. 다른 클래스 로더로로드 된 클래스는 다른 네임 스페이스에 배치됩니다. 우리는 두 개의 사용자 정의 클래스 로더를 사용하여 사용자 정의 유형을로드 할 수 있습니다 (사용자 정의 유형의 바이트 코드를 시스템 경로 또는 확장 경로에 배치하지 않으면 시스템 클래스 로더 또는 확장 클래스 로더에 의해 먼저로드됩니다). 그런 다음 획득 한 두 클래스 인스턴스를 사용하여 Java.lang.object.equals (…) 판단을 만듭니다. 이것은 동일한 사용자 정의 유형을로드 한 다음 판단을 내리기 위해 두 개의 사용자 정의 클래스 로더를 작성하여 수행 할 수 있습니다. 동시에 Java의로드를 테스트 한 다음 테스트 결과를 비교하고 테스트 할 수 있습니다.
2. 코드에서 클래스를 직접 호출하십시오 .forname (문자열 이름) 메서드. 클래스 로딩 동작을 트리거 할 클래스 로더는 무엇입니까?
class.forname (문자열 이름)은 클래스를 호출하는 클래스 로더를 기본적으로로드 클래스로 사용합니다. 해당 JDK 코드를 직접 분석 해 봅시다.
//java.lang.class.java publicstatic class <?> forname (String ClassName)은 classNotFoundException {return forname0 (className, true, className.getCallerClassLoader ());} // java.lang.classloader.java// returns 's Classload, 또는 NULL IVE NULL IVE NULL IVE NOLL. getCallerClassLoader () {// 호출 클래스 유형을 가져옵니다 (발신자) 클래스 발신자 = recincection.getCallerClass (3); // VM이 요청하는 경우 (CALLER == NULL) {returnNull; } // java.lang.class의 로컬 메소드를 호출하려면 호출 클래스 (발신자) return class.getclassloader0 ();} // java.lang.class.java//native 구현을 위해 현재 클래스 네이티브 클래스 로드러 GetClassLoader0 (getClassLoader0)의 클래스 로더를 얻으십시오. 3. 사용자 정의 클래스 로더를 작성할 때 상위 로더가 설정되지 않은 경우 상위 로더는 무엇입니까?
상위 클래스 로더가 지정되지 않은 경우 시스템 클래스 로더는 기본적으로 사용됩니다. 어떤 사람들은 이해하지 못할 수도 있습니다. 이제 JDK의 해당 코드 구현을 살펴 보겠습니다. 우리 모두 알다시피, 우리는 맞춤형 클래스 로더를 Java.lang.classloader Abstract 클래스에서 직접 또는 간접적으로 상속합니다. 매개 변수가없는 해당 기본 생성자는 다음과 같이 구현됩니다.
// java.lang.classloader.javaprotected classloader ()에서 발췌 한 {SecurityManager Security = System.GetSecurityManager (); if (security! = null) {security.checkcreateClassLoader (); } this.parent = getSystemClassLoader (); 초기화 = true;}GetSystemClassLoader () 메소드의 해당 구현을 살펴 보겠습니다.
privatestaticynchronizedVoid initsystemclassloader () {// ... sun.misc.launcher l = sun.misc.launcher.getlauncher (); scl = l.getClassLoader (); // ...}간단한 테스트 코드를 작성하여 테스트 할 수 있습니다.
System.out.println (sun.misc.launcher.getlauncher (). getClassLoader ());
이 기계의 해당 출력은 다음과 같습니다.
Sun.misc.launcher$ upclassloader@197d257
따라서 사용자 정의 클래스 로더가 부모 클래스 로더를 지정하지 않으면 기본 상위 클래스 로더가 시스템 클래스 로더라고 생각할 수 있습니다. 동시에, 우리는 다음과 같은 결론을 도출 할 수 있습니다.
인스턴트 사용자 정의 클래스 로더는 부모 클래스 로더를 지정하지 않으면 다음 세 장소의 클래스도로드 할 수 있습니다.
(1) <java_runtime_home>/lib에 따른 클래스
(2) 시스템 변수 java.ext.dir에 의해 지정된 위치의 클래스
(3) 현재 프로젝트 클래스 경로에 따른 클래스 또는 시스템 변수 java.class.path에 의해 지정된 위치
4. 사용자 정의 클래스 로더를 작성할 때 부모 클래스 로더가 널이되면 어떻게됩니까? 사용자 정의 클래스 로더가 지정된 클래스를로드 할 수없는 경우 확실히 실패합니까?
JVM 사양은 사용자 정의 클래스 로더가 부모 클래스 로더를 NULL로 강제하면 시작 클래스 로더가 현재 사용자 정의 클래스 로더의 상위 클래스 로더로 자동으로 설정 될 것이라고 규정합니다 (이 문제는 이전에 분석되었습니다). 동시에, 우리는 다음과 같은 결론을 도출 할 수 있습니다.
인스턴트 사용자 정의 클래스 로더가 부모 클래스 로더를 지정하지 않으면 <java_runtime_home>/lib 하에서 클래스를로드 할 수 있지만 <java_runtime_home>/lib/ext 디렉토리 하의 클래스는 현재로드 할 수 없습니다.
참고 : 질문 3과 4의 유추 된 결론은 java.lang.classloader.loadclass (…)의 기본 대의원 논리를 계속하는 사용자 정의 클래스 로더 자체를 기반으로합니다. 사용자 가이 기본 대의원 논리를 변경하면 위의 추론 된 결론이 유효하지 않을 수 있습니다. 자세한 내용은 질문 5를 참조하십시오.
5. 커스텀 클래스 로더를 작성할 때 일반적인 관심 요점은 무엇입니까?
(1) 일반적으로 기존로드 클래스 (…) 메소드에서 대표단 논리를 무시하지 마십시오. 일반적으로 JDK 1.2 이전 버전에서 수행되며, 그렇게하면 시스템의 기본 클래스 로더가 제대로 작동하지 않을 가능성이 높습니다. JVM 사양 및 JDK 문서 (1.2 이상 버전)에서는 사용자가 LoadClass (…) 메소드를 덮어 쓰는 것이 좋습니다. 대조적으로, 개발자는 사용자 정의 클래스 로더를 개발할 때 FindClass (…) 논리를 덮어 쓰도록 상기시켜줍니다. 문제를 확인하려면 예를 들어 :
// 사용자 커스텀 클래스 로더 잘못된 ClassLoader.java (LoadClass Logic 오버 라이드) publicClassWrongClassLoaderExtends ClassLoader {public class <?> loadclass (String Name)가 클래스 나트 FoundException {returnthis.findClass (이름); } Protected Class <?> findClass (String Name) 던지기 ClassNotFoundException {// 여기에 클래스 특정 구현 코드를로드하기 위해 프로젝트 D : /Library 이외의 특정 디렉토리에만 있다고 가정합니다.}}}}이전 분석을 통해 우리는 이미 사용자 정의 클래스 로더 (잘못된 클래스 로더)의 기본값을 알고 있습니다.
인식 된 클래스 로더는 시스템 클래스 로더이지만 네 가지 유형의 문제에 대한 결론은 현재 유효하지 않습니다. 모두가 할 수 있습니다
지금 <java_runtime_home>/lib, <java_runtime_home>/lib/ext를 테스트하십시오
프로그램 클래스 경로의 클래스를로드 할 수 없습니다.
질문 5 테스트 코드 1
publicclass frongclassloadertest {publicstaticVoid main (string [] args) {try {frongclassloader loader = new OrmentClassLoader (); 클래스 클래스로드 = loader.loadclass ( "beans.account"); System.out.println (classloaded.getName ()); System.out.println (classloaded.getClassLoader ()); } catch (예외 e) {e.printstacktrace (); }}}(참고 : D : "클래스"콩 "계정. 클래스 물리적으로 존재 함)
출력 결과 :
java.io.filenotfoundexception : D : "클래스"Java "Lang"Object.class (시스템은 지정된 경로를 찾을 수 없음)에서 java.io.fileinputstream.open (기본 메소드)에서 java.io.fileinputstream. (fileInputStream.java:106)에서 frongclass.findclass.findclass.findclass. java.lang.classinternal (java.lang.classloader.defineclass1 (기본 메소드)의 java.lang.classloader.defineclass (java.lang.classloader.defineclass1)에서 java.lang.classloader.defineclass (java.lang.classloader.defineclass.defineclass)에서 잘못된 ClassLoader.loadclass.loadclass.loadclass.loadclass.loadclass.loadclass.loadclass.loadclass.loadclass.loadclassinternal (classloader.java:319). Java.lang.classloader.defineclass (classloader.java:400) frongclassloader.findclass.findclass.findclass.loadclass.loadclass.loadclass.java:29)의 잘못된 ClassLoaderSt.MANTL "MEMO" "MEMO" java.lang.noclassdeffounderror : java.lang.classlass1 (java.lang.classlader.defineclass1)의 java/lang/객체 java.lang.classloader.defineclass (classloader.java:620)의 java.lang.classlows.defineclass (java:400). 잘못된 ClassLoader.FindClass (frongclassloader.loadclass.loadclass.loadclass.java:29)에서 잘못된 ClassLoader.LoadClass.Main (fromeclassLoaderTest.java:27)에서 잘못된 ClassLoader.findClass (잘못된 ClassLoader.java:43)
이것은로드 될 유형의 SuperType java.lang.object조차로드 할 수 없음을 의미합니다. 로드 클래스 (…)를 덮어 쓰는 것으로 여기에 나열된 논리적 오류는 분명히 비교적 간단하며 실제로 발생하는 논리적 오류는 훨씬 더 복잡 할 수 있습니다.
질문 5 테스트 2
// 사용자 커스텀 클래스 로더 잘못된 ClassLoader.java (로드 클래스 로직을 덮어 쓰지 않음) publicClassWrongClassLoaderExtends ClassLoader {Protected Class <?> FindClass (String Name) 던지기 ClassNotFoundException {// 여기에 특정 디렉토리에 단지 특정 디렉토리에 단지 구현 코드를로드하여 클래스 특정 코드를로드합니다.사용자 정의 클래스 로더 코드를 수정 한 후 Orde ClassLoader.java는 테스트 코드를 실행하면 출력 결과는 다음과 같습니다.
beans.accountwrongclassloader@1c78e57
이는 Beans.Account가 성공적으로로드되었으며 사용자 정의 클래스 로더 잘못된 클래스 로더에 의해로드됨을 보여줍니다.
나는 그 이유를 설명 할 필요가 없다고 생각합니다. 그리고 당신은 그것을 분석 할 수 있어야합니다.
(2) 위의 질문 4 및 질문 5의 분석을 통해 부모 클래스 로더를 올바르게 설정하십시오. 나는 개인적으로 이것이 사용자 클래스 로더를 사용자 정의 할 때 가장 중요한 지점이라고 생각하지만 종종 무시되거나 쉽게 가져옵니다. 이전 JDK 코드를 기본으로 분석하면 모든 사람이 지금 예제를 제시 할 수 있다고 생각합니다.
(3) FindClass (String) 메소드의 논리적 정확성을 확인하고, 클래스 로더가 정의 할로드 작업을 정확하게 이해하고 해당 바이트 코드 컨텐츠를 가장 많이 얻을 수 있는지 확인하십시오.
6. 시스템 클래스 로더가로드 할 수있는 경로를 결정하는 방법은 무엇입니까?
먼저 ClassLoader.getSystemClassLoader () 또는 기타 방법을 직접 호출하여 시스템 클래스 로더 (시스템 클래스 로더 및 확장 클래스 로더 자체가 UrlClassLoader에서 파생)를 얻을 수 있으며 UrlClassLoader에서 getUrls () 메소드를 호출하여 얻을 수 있습니다.
둘째, 시스템 속성 java.class.path를 얻어 현재 클래스 경로의 항목 정보를 직접 볼 수 있습니다. System.GetProperty ( "java.class.path")
7. 표준 확장 클래스 로더가로드 할 수있는 경로를 결정하는 방법은 무엇입니까?
방법 1 :
{url [] exturls = (((urlclassLoader)) classLoader.getSystemClassLoader (). getParent ()). geturls (); for (int i = 0; i <exturls.length; i ++) {system.out.println (exturls [i]); }} catch (예외 e) {//…}이 기계의 해당 출력은 다음과 같습니다.
파일 :/d : /demo/jdk1.5.0_09/jre/lib/ext/dnsns.jarfile :/d : /demo/jdk1.5.0_09/jre/lib/ext/localedata.jarfile :/d : /demo/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jarfile :/d : /demo/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar