제 VII VM 클래스 로딩 메커니즘 : (제 3 판) 독서 노트 (오) "자바 가상 머신에 대한 심층 이해"

제 VII VM 클래스 로딩 메커니즘 : (제 3 판) 독서 노트 (오) "자바 가상 머신에 대한 심층 이해"


독서 노트 (d)에 클래스 기록 된 파일 저장 형식은, 제 VII 약 가상 머신에로드 한 후 무엇을뿐만 아니라, 이러한 클래스 파일을로드하는 방법은 얘기했다. 라인이 장에서는 효과를 추정의 JVM 클래스 로딩에 대한 비디오를보고 서 b에 가서 더 좋은 식사를 마친 ...

구문 분석 및 변환, 자바 가상 머신로드 데이터를 메모리에 클래스 파일에서 클래스를 설명하고, 데이터 유효성 검사 : 텍스트의 시작하기 전에, 가상 머신의 클래스 로딩 메커니즘 소위의 계정을 제공합니다 결국 자바 클래스 로딩 메커니즘의 유형을 형성 초기화,이 프로세스는 가상 기계, 가상 머신에서 직접 사용할 수 있습니다.

클래스 로딩 시간

가상 머신의 유형을 시작하면 메모리를 언로드까지 (전체 수명주기 부하 (로드), 분석 확인 (검증), 준비 (준비)를 통해 해상도를 갈 것이다, 메모리에로드 ), 초기 설정 (초기화)를 사용하십시오 (사용) 및 언로드 (언로드)하는 검증 제제 집합 연결된 세 부분을 파싱 (연결 7 단계).

로드, 검증, 준비, 초기화 및 하역 순서 다섯 단계는 순서로 반드시 구문 분석 단계를 시작해야 단계 프로세스에 의해로드 단계의 유형을 결정하는 것입니다. 이 초기화 단계 이후에 시작할 수 있습니다.

"자바 가상 머신 사양"엄격한 규칙이 있습니다 만 육가지 경우 즉시 클래스가 初始化(부하, 검증이 전에 시작하는 자연의 필요성을 준비) :

  • 종류가 초기화되지 않은 경우 시간이 처벌 할 필요가 초기화 단계, 네 개의 명령어는 일반적인 시나리오를 생성 할 수 있다는 새로운 getstatic, putstatic, invokestatic (이) 4 개 바이트 코드 지침을 만남 :

    • 때 개체의 인스턴스를 새로운 키워드를 사용하여
    • 읽기 또는 정적 필드 타입 세트 (최종 수정 컴파일러 정적 필드를 제외한 상수 풀로 결과 됨) 때
    • 시간의 유형의 정적 메소드를 호출
  • 전화의 유형을 반영하기 위해 java.lang.reflect의 패키지 방식을 사용하는 경우 유형이 초기화되어 있지 않은 경우, 당신은 초기화를 실행해야합니다.

  • 우리는 부모 클래스가 초기화되지 않은 발견 한 경우, 클래스 초기화 시간은, 당신은 부모 클래스의 트리거 초기화해야 할 때

  • 가상 머신이 시작되면, 사용자의 요구가 실행하는 메인 클래스를 지정, 가상 머신이 마스터 클래스를 초기화

  • 인터페이스가 기본 방법 JDK8 신규 진입을 정의 할 때 인터페이스의 구현 클래스의 초기화가 발생하면, 그것은 전에 초기화 할이 인터페이스가된다.

  • 최종 분석 결과 java.lang.invoke.MethodHandle 예 REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecial 핸들 경우 네 가지 방법의 유형 및 초기화되지 대응이 방법 핸들 클래스는 초기화를 트리거 할 필요가있다.

    인터페이스는 또한로드 프로세스를 가지며, 클래스 로딩 프로세스가 동일하지 않은 로컬 인터페이스와 클래스 사이의 차이는 상기 여섯 가지 "유일한"이다 초기화는 세 번째 시나리오를 실행하는 데 필요한 : 클래스 초기화가 요구되는 때 모든 부모 클래스가 초기화되었지만, 초기화하는 동안 인터페이스는 인터페이스는, 모든 초기화를 완료하기 위해 부모를 필요로하지 않는 부모 인터페이스가 초기화됩니다 사용하여 실시간으로 .

클래스로드 프로세스

하중

첫째, 클래스의 완전한 이름을 통해 이러한 바이너리 바이트 스트림의 정의를 얻을 후 정적 스토리지의 바이트 스트림을 나타냅니다 :로드 과정에서, JVM은 주로 세 가지를 수행하는 런타임 데이터 구조 영역 방법으로 구조, 마지막이 java.lang.Class 클래스 오브젝트의 대표이 클래스의 각종 데이터 입력 영역에 액세스하는 방법을 메모리에 생성한다.

  • 어레이 형 구성 요소 (구성 요소 유형, 치수,주의를 제거하고 전방 소자 형 영역을 분리 배열 유형으로 지칭) 참조 형식이면 본 장착부의 반복적 사용은, 정의 된 구성 요소 타입을로드 배열은 구성 요소 클래스 로더 클래스 이름 공간의 로딩 유형에 식별됩니다.
  • 컴포넌트 타입이 배열 참조 형 (성분 int 타입에 대한 예를 들어, INT [] 배열)이 아닌 경우, Java 가상 머신은 표시된 배열 부트 클래스 로더와 연관이 .
  • 접근성 컴포넌트 타입의 접근성과 일치 Array 클래스 구성 요소 유형이 참조 형이 아닌 경우, 접근성 배열 클래스는 것이다 대중에게 기본 , 모든 클래스와 인터페이스에 액세스 할 수 있습니다.
  • 로딩 단계 이후, Java 가상 머신의 외부 이진 바이트 스트림은 프로세스 존에 설치되어있는 가상 머신에 저장 포맷에 따라, 메소드 영역 데이터 저장 포맷은 전체적으로 가상 머신 자체 "자바 가상 머신에 의해 정의하여 구현 이 분야에 대한 사양 "특정 데이터 구조는 소정 아니다. 적절한 배치 형 데이터 영역 법 후 Java 힙 메모리 java.lang.Class 클래스의 객체, 데이터 종별 영역의 외부 인터페이스 프로그램 액세스 방식으로서 오브젝트를 인스턴스화한다.
  • 로드 상과 조작 부분의 연결 단계 (예를 들어, 파일 형식의 바이트 코드 검증 작업의 일부), 로딩 단계가 완료되지 않은이 연결 단계가 시작 수 있습니다 교차 수행하지만,이 작업의 로딩 단계에서 잡은 여전히 ​​연결입니다 스테이지,이 두 단계는 고정 된 순서를 유지 시작 시간의 일부.
확인

클래스 정보 파일이 바이트 스트림은 모든 제약 정보가 자신의 안전을 위태롭게하지 않고 가상 머신을 실행할 수있는 코드로 처리됩니다 보장하기 위해, "자바 가상 머신 사양"을 요구 충족이 포함되어 있는지 확인합니다.

준비된

준비 단계 정식 클래스 변수 (즉, 정적 변수, 정적 변수 수정)이 메모리를 할당하고, 스테이지 클래스 변수의 초기 값을 세트에 정의된다. 이 시간 메모리 할당은 클래스 변수를 포함하고, 인스턴스 변수는 객체가 자바 힙에서 인스턴스화 된 객체와 함께 할당됩니다 인스턴스 변수를 포함하지 않습니다.

해결

직접 참조를 교체하는 과정에 대한 기호 참조 자바 가상 머신 상수 풀.

초기화

초기화 단계는 클래스 생성자의 실행 <clinit>()프로세스 접근 방법을. <clinit>()방법 프로그래머는 자동으로 제품 javac 컴파일러이며, 직접 자바 코드를 작성하지 않습니다.

<clinit>()이는 합병 순차적 컴파일러 명령문에 의해 수집 된 모든 클래스 및 정적 변수 할당 문 블록 조작 (정적 {} 블록) 문은 컴파일러 클래스가 자동으로 수집 소스 파일의 외관의 순서를 결정, 블록 정적 문은 이전 문장 블록에 정의 된 정적 변수에만 액세스, 정적 블록의 앞 뒤에 변수의 정의는, 할당 할 수 있습니다,하지만 액세스 할 수 없습니다.

<clinit>()클래스 생성자의 방법 (가상 머신의 인스턴스 즉은, 원근에 구성되어있는 <init>()다른 방법), 명시 적으로 보장하기 위해 부모 클래스 생성자, 자바 가상 머신을 호출하지 않는 서브 클래스의 <clinit>()방법 상위 클래스의 실행 전에 <clinit>()() 메소드의 실행이 완료된 . 따라서, 제 Java 가상 머신 <실행 <clinit>()방법의 유형 java.lang.Object를해야한다. 정적으로 정의 된 부모 클래스 문 블록이 우선 서브 클래스 변수 할당을합니다.

클래스가 문장의 정적 블록이 아닌 경우, 변수의 더 할당이없는, 다음 컴파일러는 클래스 생성 할 수 없습니다 <clinit>()방법.

인터페이스 블록 정적 문을 사용할 수 없지만, 여전히 가변 할당이 초기화되고, 따라서 동일한 클래스 인터페이스의 생성 <clinit>()방법. 그러나, 그 차이는 클래스 인터페이스는, 인터페이스를 구현 <clinit>()상위 인터페이스 실행할 필요하지 않은 <clinit>()인터페이스에 정의 된 변수를 사용할 때만 부모 때문에, 방법은 초기화 상위 인터페이스 일 것이다. 또한, 인터페이스가 같은 경우, 초기화 아니다 구현하는 인터페이스 구현 클래스 <clinit>()방법.

Java 가상 머신이 있어야 클래스 있도록 <clinit>()방법을 올바르게 멀티 스레드 환경에서 잠겨 동기화 여러 스레드 클래스를 초기화하는 경우, 만,이 클래스를 수행하는 하나 개의 스레드가있을 것입니다 <clinit>()방법은 다른 스레드는 활성 스레드가 완료 될 때까지 기다릴 필요가 차단 <clinit>()방법.

클래스 로더

클래스 로더 클래스 계층 구조,은 OSGi, 뜨거운 배포를 나누어, 코드 암호화 및 기타 분야는 빛, 자바 기술은 디자인 애플릿의 요구를 충족하기 위해 원래 있던 시스템의 중요한 초석이되었다 지금 애플릿 (브라우저 측면을) 제거.

클래스 및 클래스 로더

모든 클래스의 경우, 공동 단지에와 자바 가상 머신에서의 고유성을 가지는 클래스 자체가, 클래스는 두 개의 "동일한"를 비교하는 것입니다로드 된 클래스 로더에 의해 설정되어야합니다 이 두 클래스는, 그렇지 않으면, 같은 파일에서이 두 클래스 클래스는 자바 가상 머신에로드되는 경우에도, 한 클래스 로더로 다른로드, 차종이 같은 클래스 로더를로드하는 것이 전제하에 만들어진 이 두 클래스는 반드시 동일하지 않습니다.

이 개념은 내가 슈슈를 재현하는 코드를 본 것은 이번이 처음이다.

public class ClassLoaderTest {

	public static void main(String[] args) throws Exception {

		ClassLoader myLoader=new ClassLoader() {
			@Override
			public Class<?> loadClass(String name) throws ClassNotFoundException {
				try {
					String filaName=name.substring(name.lastIndexOf(".")+1)+".class";
					InputStream is=getClass().getResourceAsStream(filaName);
					if(is==null){
						return super.loadClass(name);
					}
					byte[] b=new byte[is.available()];
					is.read(b);
					return  defineClass(name,b,0,b.length);
				} catch (IOException e) {
					throw  new ClassNotFoundException(name);
				}
			}
		};

		Object obj=myLoader.loadClass("exam.offer.day05.ClassLoaderTest").newInstance();

		System.out.println(obj.getClass());
		System.out.println(obj instanceof exam.offer.day05.ClassLoaderTest);
	}
}

결과 :

class exam.offer.day05.ClassLoaderTest
false

위의 코드는 간단한 클래스 로더,로드 클래스와 exam.offer.day05.ClassLoaderTest 및 인스턴스로를 구성하는 것입니다.

Java 가상 머신이 ClassLoaderTest 두 개의 클래스가 존재, 하나의로드 가상 머신 클래스 로더의 응용 프로그램이며, 다른 하나는하지만, 우리의 정의를로드하는 클래스 로더입니다 그들은 같은 클래스 파일에서 온,하지만 자바 가상 머신에서 여전히 두 개의 상호 독립적 인 클래스이며, 결과는 자연적인 개체를 확인하는 것은 거짓에 속하는 입력 않습니다.

부모 위임 모델

Java 가상 머신의 관점에서, 두 개의 서로 다른 클래스 로더가 부트 클래스 로더 (BootstrapClassLoader)는 C ++ 구현, 가상 머신의 일부를 사용하고, 하나는 자바로 구현 된 다른 클래스 로더, 모든 가상 시스템 외부에 독립적으로 존재하고, 모든 추상 java.lang.ClassLoader 클래스에서 파생.

상위 클래스 로딩 위임 모델의 계층 구조는 아래 그림과 같이 :

IMG

부트 클래스 로더 (부트 스트랩 클래스 로더) : 적재에 대한 책임은 <JAVA_HOME> \ lib 디렉토리에 저장, 또는 -Xbootclasspath경로에 저장된 매개 변수에 의해 지정된 및 Java 가상 머신은 식별 라이브러리 할 수있다 가상 머신이 메모리에로드.

확장 클래스 로더 (확장 클래스 로더) :이 클래스는 sun.misc.Launcher $ ExtClassLoader 자바 코드의 형태로 구현되는 클래스 로더입니다. 그것은로드 <JAVA_HOME> \ lib에 ext 디렉토리 또는 지정 java.ext.dirs 시스템 변수 경로 \ 라이브러리의 모든 책임이 있습니다.

응용 프로그램 클래스 로더 (응용 프로그램 클래스 로더) : sun.misc.Launcher $ AppClassLoader에 의해 구현되는 로더이 클래스. 응용 프로그램 클래스 로더가 getSystem가-클래스 로더 클래스 로더 클래스 반환 값 () 메소드이기 때문에, 그래서 어떤 경우에는 "시스템 클래스 로더."라는 것을 이 사용자 클래스 패스 (클래스 경로)에 모든 라이브러리를로드 할 책임이있다.

공정 작업의 부모 위임 모델 입니다 : 클래스 로더가로드하는 클래스가 요청을받은 경우, 먼저하지이 클래스를로드하려고하지만 부모 클래스 로더에 요청을 위임 자신의 수행 모든 부하 요청이 최종 최상위 부트 로더 클래스에 전송해야하므로 클래스 로더의 각 레벨을 완료하는 것은, 사실, 부모 로더 의견들이 로딩 요청을 완료 할 수 없습니다 만 (의 검색 필요한 클래스를) 찾을 수 없을 때, 아이 로더는 자신의 부하를 완료하려고합니다.

왜 부모는 모델을 위임? 안전, 수 하중을 피가 변경되기 핵심 클래스의 중복을 피하기 . 우리가 클래스 경로 년이라는 java.lang.Object 상위 클래스를 작성하면, 시스템은 Object 클래스의 숫자에 나타납니다,하지만이 클래스는 일반적으로 컴파일 할 수 있지만, 위임 모델의 부모를 사용하지만 실행로드 할 수 없습니다. (가) 먼저 가장 상위의 클래스 java.lang.Object 클래스 로더 로딩 시스템이기 때문에 궁극적 인 사용자 정의 클래스 로더는 java.lang.Object 상위 클래스를로드 할 수 없습니다.

특히 전체 업무 프로세스를 이해하기 위해, 모양과 소스 코드, 소스 코드 및 주석 반면에 쉽게 찾고.

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
파괴 부모 위임 모델

세 번 전체 손상이 있습니다.

-> 첫 번째 손상 : 부모 위임 모델이 나타나기 전에 발생합니다. () 부모 위임 모델은 JDK1.2 이후 대두되고 있지만, 사용자 정의 클래스 로더는 기존 코드와의 호환성을 위해 부모 위임 모델, 당신 만의 java.lang.ClassLoader의에서 새로운 보호 방법 (때문에) findClass를 추가 할 수 있도록, 존재하고있다 이 메소드를 재정의 할 수 가이드보다 오히려로 loadClass (), 사용자 클래스 로더 로직에 의해 제조. (때문에) findClass ()를 오버라이드 (override)하는 경우, 다음과 유사한 수행하는 클래스와 클래스 로더 코드의 일부는, 인쇄 결과에서 발견 될 수있는 것은 사실이다. 테스트는 예를 찾아보실 수 있습니다 -> 부모 위임 모델을 파괴하는 방법

-> 두 번째 파괴 : 모델 자체에 결함이 발생 부모 (상위로드의 기본 클래스 로더) 클래스를 각 클래스 로더를 기본 같은 문제에 좋은 솔루션을 지정, 기본 그들은 항상 API 사용자 코드로 호출되기 때문에 클래스는 "기초"라고하지만, 일이 종종 절대적으로 완벽하지 않습니다.

당신은 사용자 코드에 대한 기본 클래스 다시 다음을 호출 할 경우 무엇을 할까?

전형적인 예는, JNDI는 이제 표준 자바 서비스 (JDK1.3 때의 rt.jar에서 그들에) 부하에 부트 클래스 로더에서 해당 코드,하지만 JNDI는 JNDI 서비스입니다 목적은 구현하고 독립적 인 공급 업체에서 클래스 경로 애플리케이션에서 JNDI 인터페이스 제공자를 호출하는 코드를 배포해야하지만 부트 클래스 로더는 이러한 코드 "를 알 수 없습니다", 관리 자원과 모양을 중앙 집중화하는 것입니다.

쓰레드 컨텍스트 클래스 로더 (쓰레드 컨텍스트 클래스 로더) :이 문제를 해결하기 위해, 자바 디자인 팀은 덜 우아한 디자인을 도입했다. 이 클래스 로더는 setContextClassLoader () 메소드 java.lang.Thread의 클래스를 사용하면 스레드를 만들 때 응용 프로그램을 통해 전 세계적으로 설정하지 않은 경우, 그 부모 스레드에서 상속 설정되어 있지 않은 설정할 수 있습니다 다음 클래스 로더는 기본 응용 프로그램 클래스 로더입니다.

로더 스레드 컨텍스트로, JNDI 서비스는 하위 클래스 로딩 작업을 완료 할 부모 클래스 로더 클래스 로더의 요청이의 실제 동작 인 필요한 SPI 코드를로드하는 데 사용할 수 있습니다 문제는 실제로 일반 원칙 부모 위임 모델을 위반 한 클래스 로더를 사용하여 반대로 부모 위임 모델의 계층 구조를 통해 얻을 수 있습니다뿐만 아니라, 무기력. 모든 자바로드 작업은 JNDI, 예를 들어, 기본적으로 SPI이 방법을 포함 는 JDBC 의 JCE, JBI JAXB 등이있다.

-> 세 번째 파괴 : 핫 코드 교체 모듈 핫 배포, 짧은 대답 : 사용자 역학이 발생하기 때문에 프로그램의 추구, 여기에 "동적"이라고 현재에 매우 "뜨거운"명사를 의미 그만큼 배포가 사용할 수 있습니다로, 컴퓨터를 다시 시작하지 않고 말을하는 것입니다.

JDBC에, 예를 들어, 부모 위임 모델의 예는 정교한 파괴된다. 기본 JDBC 유형이 BootstrapClassLoader하여 패키지의 rt.jar에 배치되고, 동적으로 데이터베이스 드라이버 클래스의 다른 유형을로드하고 MySQL의 커넥터 - .JAR 드라이버 클래스가되어있는 JDBC 드라이버 클래스의 필요성을로드 사용자는 내가 작성한 코드이기 때문에, 그것은 부모를 위임 된이 모델을 해결할 수있는 스레드 컨텍스트 클래스 로더에 의해 응용 프로그램 클래스 시작 클래스를로드 할 필요가 확실히 클래스 로더를 시작합니다 자신의 코드를로드 할 수 없습니다 쓰기 부드러운로드의 위반.

자바 모듈 시스템

JDK9 모듈 시스템이 도입된다. 모듈 형 캡슐이 사용 가능한 경우, 모듈 수 를 명시 적으로 선언 다른 모듈에 의존하는 Java 가상 머신을 시작할 수 있도록 확인하는 좋은 설정 응용 프로그램 개발 단계를 종속 런타임에 완료를 없는 경우 있음, 따라서 그것은 인해 비정상적으로 인한 의존의 유형으로 실행 큰 부분을 피하고, 직접 시작하지 못했습니다.

클래스 로더 모듈

1. 확장 클래스 로더 (확장 클래스 로더) 대체 클래스 로더 플랫폼 (플랫폼 클래스 로더)

2. 플랫폼 클래스 로더 및 응용 프로그램 클래스 로더는 더 이상 이는 java.net.URLClassLoader에서 파생되지 않습니다

게시 58 개 원래 기사 · 원 찬양 5 · 조회수 6240

추천

출처blog.csdn.net/weixin_40992982/article/details/104055126