자바 가상 머 신 같은 로드

클래스 로드 프로 세 스
클래스 로드 프로 세 스 는 간단하게 세 단계 로 나 눌 수 있 습 니 다.
  • 로드
  • 연결
  • 초기 화
  • 그 중의 연결 은 세 단계 로 나 눌 수 있다.
  • 검증
  • 준비
  • 해석
  • 다음은 각 절차 에 대해 각각 소개 하 겠 습 니 다.
    1.1 클래스 로드 조건
    클래스 연결 절 차 를 이해 하기 전에 트리거 클래스 로 딩 조건 을 살 펴 보 세 요.JVM 은 무조건 클래스 를 불 러 오지 않 습 니 다.한 클래스 나 인터페이스 가 처음 사용 할 때 만 초기 화 되 어야 합 니 다.여기 서 의 사용 은 주동 적 으로 사용 하 는 것 을 말 하 는데 주동 적 으로 사용 하 는 것 은 다음 과 같은 상황 을 포함한다.
  • 클래스 의 인 스 턴 스 를 만 들 때:예 를 들 어 new 을 사용 하여 만 들 거나 반사,복제,반 직렬 화
  • 을 사용 합 니 다.
  • 클래스 의 정적 방법 을 호출 할 때:예 를 들 어 invokestatic 명령
  • 을 사용 합 니 다.
  • 사용 클래스 또는 인터페이스의 정적 필드:예 를 들 어 getstatic/putstatic 명령
  • 을 사용 합 니 다.
  • java.lang.reflect 의 반사 류 방법 을 사용 할 때
  • 초기 화 자 류 시 부모 류
  • 을 초기 화 해 야 합 니 다.
  • main() 방법 이 함 유 된 클래스
  • 상기 상황 을 제외 하고 다른 상황 은 수 동적 으로 사용 되 며 클래스 의 초기 화 를 일 으 키 지 않 습 니 다.
    예 를 들 어 다음 의 예:
    
    public class Main {
        public static void main(String[] args){
            System.out.println(Child.v);
        }
    }
    
    class Parent{
        static{
            System.out.println("Parent init");
        }
        public static int v = 100;
    }
    
    class Child extends Parent{
        static {
            System.out.println("Child init");
        }
    }
    
    출력 은 다음 과 같 습 니 다:
    Parent init
    100
    그리고 클래스 로드 매개 변수 -XX:+TraceClassLoading 을 더 하면 Child 이 불 러 온 것 을 볼 수 있 습 니 다.
    
    [0.068s][info   ][class,load] com.company.Main
    [0.069s][info   ][class,load] com.company.Parent
    [0.069s][info   ][class,load] com.company.Child
    Parent init
    100
    
    하지만 초기 화 는 이 뤄 지지 않 았 다.또 다른 예 는 final 에 관 한 것 으로 코드 는 다음 과 같다.
    
    public class Main {
        public static void main(String[] args){
            System.out.println(Test.STR);
        }
    }
    
    class Test{
        static{
            System.out.println("Test init");
        }
        public static final String STR = "Hello";
    }
    
    출력 은 다음 과 같 습 니 다:
    [0.066s][info   ][class,load] com.company.Main
    HelloTest 류 는 로드 되 지 않 았 습 니 다.final 이 최적화 되 었 기 때문에 컴 파일 된 Main.class 에서 Test 류 를 인용 하지 않 았 습 니 다.
    
    0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    3: ldc           #4                  // String Hello
    5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    
    바이트 코드 오프셋 3 의 위치 에서 ldc 을 통 해 상수 탱크 4 항 을 스 택 에 넣 습 니 다.이때 바이트 파일 에서 상수 탱크 4 항 은 다음 과 같 습 니 다.
    
    #3 = Class              #24            // com/company/Test
    #4 = String             #25            // Hello
    #5 = Methodref          #26.#27        // java/io/PrintStream.println:(Ljava/lang/String;)V
    
    따라서 Test 종 류 를 불 러 오지 않 고 상수 탱크 의 상수 만 직접 참조 하기 때문에 출력 에는 Test 의 로 딩 로그 가 없습니다.
    1.2 로드
    클래스 로 딩 시 JVM 은 다음 작업 을 완료 해 야 합 니 다:
  • 클래스 의 전체 이름 을 통 해 바 이 너 리 데이터 흐름
  • 가 져 오기
  • 분석 류 의 이 진 데이터 흐름 은 방법 구역 내의 데이터 구조
  • 이다.
  • java.lang.Class 류 의 인 스 턴 스 를 만 들 고 이 유형
  • 을 나타 낸다.
    첫 번 째 단 계 는 바 이 너 리 데이터 흐름 을 가 져 옵 니 다.경로 가 많 습 니 다.예 를 들 어:
  • 바이트 파일
  • JAR/ZIP 압축 팩
  • 네트워크 에서
  • 을 불 러 옵 니 다.
    등등,이 진 데이터 흐름 을 가 져 온 후 JVM 을 처리 하고 java.lang.Class 인 스 턴 스 로 전환 합 니 다.
    1.3 검증 하 다.
    검 증 된 작업 은 불 러 온 바이트 코드 가 합 법 적 이 고 합 리 적 이 며 규범 화 된 것 을 확보 하 는 것 입 니 다.절 차 는 다음 과 같다.
    在这里插入图片描述
  • 형식 검사:바 이 너 리 데이터 가 형식 요구 와 규범 에 부합 되 는 지 판단 합 니 다.예 를 들 어 마수 로 시작 하 는 지,메 인 버 전 번호 와 작은 버 전 번호 가 현재 JVM 지원 범위 내 에 있 는 지 등
  • 입 니 다.
  • 의미 검사:예 를 들 어 모든 유형 에 아버지 류 가 존재 하 는 지,final 으로 정의 되 는 방법 이나 유형 이 다시 로드 되 거나 계승 되 었 는 지,호 환 되 지 않 는 방법 이 존재 하 는 지 등
  • 바이트 코드 검증:바이트 코드 흐름 에 대한 분석 을 통 해 바이트 코드 가 정확하게 실 행 될 수 있 는 지 판단 하려 고 합 니 다.예 를 들 어 존재 하지 않 는 명령 으로 넘 어 갈 수 있 는 지,함수 호출 이 정확 한 매개 변 수 를 전 달 했 는 지 등 이지 만 바이트 코드 가 안전하게 실 행 될 수 있 는 지 100%판단 할 수 없습니다.다만 가능 한 한 예측 할 수 있 는 뚜렷 한 문 제 를 검사 할 수 있 습 니 다.검 사 를 통과 하지 못 하면 이 종 류 를 불 러 오지 않 습 니 다.검 사 를 통과 하면 이 종 류 는 전혀 문제 가 없다 는 것 을 설명 할 수 없습니다.
  • 기호 참조 검증:클래스 나 방법 이 확실히 존재 하 는 지 확인 하고 현재 클래스 가 이러한 데 이 터 를 방문 할 수 있 는 권한 이 있 는 지 확인 합 니 다.예 를 들 어 하나의 종 류 를 찾 지 못 하면 NoClassDefFoundError 을 던 지고 방법 을 찾 지 못 하면 NoSuchMethodError
  • 을 던 집 니 다.
    1.4 준비 하 다.
    클래스 가 검증 을 통과 하면 준비 단계 에 들 어 갑 니 다.이 단계 에서 JVM 은 클래스 에 해당 하 는 메모리 공간 을 분배 하고 초기 값 을 설정 합 니 다.예 를 들 어:
  • int 초기 화 0
  • long 초기 화 0L
  • double 초기 화 0f
  • 인용 초기 화 null
  • 상수 필드 가 존재 한다 면 이 단계 도 상수 값 을 부여 합 니 다.
    1.5 해석 하 다.
    해석 은 클래스,인터페이스,필드 와 방법의 기호 인용 을 직접 인용 으로 바 꾸 는 것 이다.기호 인용 은 일부 글자 의 인용 으로 JVM 의 메모리 데이터 구조 와 메모리 구조 와 무관 하 다.바이트 파일 에서 상수 탱크 를 통 해 대량의 기호 인용 을 했 기 때문에 이 단 계 는 이러한 인용 을 직접 인용 으로 바 꾸 어 클래스,필드,방법 이 메모리 에 있 는 지침 이나 직접 오프셋 을 얻 는 것 이다.
    또한 문자열 은 매우 중요 한 역할 을 하기 때문에 JVMString 에 대해 특별한 처 리 를 했 고 문자열 의 상수 를 직접 사용 할 때 클래스 에 CONSTANT_String 이 나타 나 고 CONSTANT_UTF8 상수 항목 을 참조 할 것 이다.JVM 이 실 행 될 때 내부 상수 탱크 에 서 는 문자열 구속 표(intern)를 유지 하고 그 안에 나타 난 모든 문자열 의 상수 와 중복 항목 을 저장 합 니 다.String.intern() 을 사용 하면 다음 코드 와 같은 문자열 을 구속 표 에 인용 할 수 있 습 니 다.
    
    public static void main(String[] args){
        String a = 1 + String.valueOf(2) + 3;
        String b = "123";
        System.out.println(a.equals(b));
        System.out.println(a == b);
        System.out.println(a.intern() == b);
    }
    
    출력:
    true
    false
    true
    여기 서 b 은 상수 자체 이기 때문에 a.intern() 은 구속 표 인용 후 b 자 체 를 되 돌려 비교 한 결과 진실 이 었 다.
    1.6 초기 화
    초기 화 단 계 는 클래스 의 초기 화 방법 <clint>,<clint> 은 컴 파일 기간 에 생 성 되 고 정적 구성원 의 할당 문 과 static 문 이 공동으로 생 성 됩 니 다.
    또한,한 종 류 를 불 러 올 때 JVM 은 항상 이러한 종류의 부 류 를 불 러 오 려 고 하기 때문에 부 류 는 <clint> 방법 이 항상 하위 클래스 의 <clint> 방법 전에 호출 된다.다른 한편,주의해 야 할 것 은 <clint> 이 다 중 스 레 드 환경 에서 의 안전성 을 확보 하 는 것 이다.즉,여러 스 레 드 가 같은 유형 을 동시에 초기 화 할 때 한 스 레 드 만 <clint> 방법 에 들 어 갈 수 있다.다시 말 하면 다 중 스 레 드 에서 잠 금 이 생 길 수 있다.예 를 들 어 다음 코드:
    
    package com.company;
    
    import java.util.concurrent.TimeUnit;
    
    public class Main extends Thread{
        private char flag;
        public Main(char flag){
            this.flag = flag;
        }
        
        public static void main(String[] args){
            Main a = new Main('A');
            a.start();
            Main b = new Main('B');
            b.start();
        }
    
        @Override
        public void run() {
            try{
                Class.forName("com.company.Static"+flag);
            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }
        }
    }
    
    class StaticA{
        static {
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            try{
                Class.forName("com.company.StaticB");
            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }
            System.out.println("StaticA init ok");
        }
    }
    
    class StaticB{
        static {
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            try{
                Class.forName("com.company.StaticA");
            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }
            System.out.println("StaticB init ok");
        }
    }
    
    StaticA 을 불 러 올 때 StaticB 을 불 러 오 려 고 시 도 했 으 나 StaticB 이 불 러 왔 기 때문에 StaticA 을 불 러 오 는 스 레 드 는 Class.forName("com.company.StaticB") 곳 으로 막 히 고 같은 이치 로 StaticB 을 불 러 오 는 스 레 드 는 Class.forName("com.company.StaticA") 곳 으로 막 혀 서 자물쇠 가 생 겼 다.
    2.ClassLoader
    2.1 ClassLoader 소개ClassLoader 은 클래스 로 딩 의 핵심 구성 요소 로 모든 ClassClassLoader 으로 로드 되 고 ClassLoader 은 다양한 방식 으로 Class 정보의 바 이 너 리 데이터 흐름 을 시스템 에 읽 어 들 인 다음 JVM 에 연결,초기 화 등 작업 을 한다.따라서 ClassLoader 담당 클래스 의 로드 프로 세 스 는 ClassLoader 을 통 해 클래스 의 연결 과 초기 화 행 위 를 변경 할 수 없습니다.ClassLoader 은 추상 적 인 유형 으로 중요 한 인터페이스 정의 로드 절차 와 로드 방식 을 제공 했다.주요 한 방법 은 다음 과 같다.public Class<?> loadClass(String name) throws ClassNotFoundException:클래스 이름 을 지정 하고 클래스 를 불 러 옵 니 다.이 클래스 의 Class 인 스 턴 스 를 되 돌려 줍 니 다.이상 을 찾 을 수 없습니다.protected final Class<?> defineClass(byte[] b, int off, int len):주어진 바이트 흐름 에 따라 하나의 클래스 를 정의 합 니 다.offlen 은 바이트 배열 의 오프셋 과 길 이 를 표시 합 니 다.이것 은 protected 방법 으로 사용자 정의 하위 클래스 에서 만 사용 할 수 있 습 니 다.protected Class<?> findClass(String name) throws ClassNotFoundException:하나의 종 류 를 찾 으 면 loadClass 에서 호출 되 어 사용자 정의 검색 류 의 논리 에 사 용 됩 니 다.protected Class<?> findLoadedClass(String name):불 러 온 클래스 를 찾 습 니 다.
    2.2 클래스 로 더 분류
    표준 Java 프로그램 에서 JVM 은 3 가지 로 더 를 만들어 전체 응용 프로그램 에 서 비 스 를 제공 합 니 다.각각:
  • 작 동 클래스 로 더:Bootstrap ClassLoader
  • 확장 클래스 로 더:Extension ClassLoader
  • 응용 클래스 로 더(시스템 클래스 로 더 라 고도 함):App ClassLoader
  • 또한 프로그램 에서 자신의 클래스 로 더 를 정의 할 수 있 습 니 다.전체적으로 볼 때 차원 구 조 는 다음 과 같 습 니 다.
    在这里插入图片描述
    일반적으로 각 로 더 가 담당 하 는 범 위 는 다음 과 같다.
  • 가동 클래스 캐리어:로드 시스템 의 핵심 클래스 를 책임 집 니 다.예 를 들 어 rt.jar 패키지 의 클래스
  • 확장 클래스 로 더:lib/ext/*.jar 의 클래스
  • 을 불 러 옵 니 다.
  • 응용 클래스 로 더:사용자 프로그램 을 불 러 오 는 클래스
  • 사용자 정의 로 더:특수 경 로 를 불 러 오 는 클래스 입 니 다.보통 사용자 프로그램의 클래스
  • 입 니 다.
    2.3 양친 위임
    기본 적 인 상황 에서 클래스 로 딩 은 부모 위임 로 딩 모드 를 사용 합 니 다.구체 적 으로 말 하면 클래스 가 로 딩 되 었 을 때 현재 클래스 가 로 딩 되 었 는 지 여 부 를 판단 합 니 다.로 딩 되 었 다 면 로 딩 된 클래스 로 돌아 갑 니 다.없 으 면 먼저 부모 에 게 로 딩 을 요청 합 니 다.부모 도 같은 절차 에 따라 로 딩 여 부 를 판단 합 니 다.만약 에 여기 서 부모 에 게 로 딩 을 의뢰 하지 않 았 다 면부모 가 불 러 오 는 데 실패 하면 스스로 불 러 옵 니 다.
    在这里插入图片描述
    위의 그림 에서 클래스 로 더 를 사용 하 는 부 모 는 확장 클래스 로 더 입 니 다.확장 클래스 로 더 를 사용 하 는 부 모 는 시작 클래스 로 더 입 니 다.시스템 이 종 류 를 불 러 올 필요 가 있 을 때 바 텀 클래스 로 더 부터 판단 합 니 다.불 러 올 필요 가 있 을 때 맨 위 에서 불 러 오고 로드 가 성공 할 때 까지 순서대로 아래로 시도 합 니 다.
    모든 로 더 에서 시작 클래스 로 더 는 가장 특별 합 니 다.Java 언어 로 이 루어 진 것 이 아니 라 Java 에서 대상 과 대응 하지 않 습 니 다.시스템 핵심 클래스 는 시작 클래스 로 더 를 불 러 옵 니 다.다시 말 하면 프로그램 에서 시작 클래스 로 더 를 가 져 오 려 고 시도 하면 null 을 얻 을 수 있 습 니 다.
    
    System.out.println(String.class.getClassLoader() == null);
    출력 결과 가 진짜 입 니 다.
    자바 가상 머 신 같은 로 딩 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 JVM 류 로 딩 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기