자바 가상 머 신 같은 로드
클래스 로드 프로 세 스 는 간단하게 세 단계 로 나 눌 수 있 습 니 다.
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
Hello
Test
류 는 로드 되 지 않 았 습 니 다.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
으로 정의 되 는 방법 이나 유형 이 다시 로드 되 거나 계승 되 었 는 지,호 환 되 지 않 는 방법 이 존재 하 는 지 등 NoClassDefFoundError
을 던 지고 방법 을 찾 지 못 하면 NoSuchMethodError
1.4 준비 하 다.
클래스 가 검증 을 통과 하면 준비 단계 에 들 어 갑 니 다.이 단계 에서
JVM
은 클래스 에 해당 하 는 메모리 공간 을 분배 하고 초기 값 을 설정 합 니 다.예 를 들 어:int
초기 화 0
long
초기 화 0L
double
초기 화 0f
null
1.5 해석 하 다.
해석 은 클래스,인터페이스,필드 와 방법의 기호 인용 을 직접 인용 으로 바 꾸 는 것 이다.기호 인용 은 일부 글자 의 인용 으로
JVM
의 메모리 데이터 구조 와 메모리 구조 와 무관 하 다.바이트 파일 에서 상수 탱크 를 통 해 대량의 기호 인용 을 했 기 때문에 이 단 계 는 이러한 인용 을 직접 인용 으로 바 꾸 어 클래스,필드,방법 이 메모리 에 있 는 지침 이나 직접 오프셋 을 얻 는 것 이다.또한 문자열 은 매우 중요 한 역할 을 하기 때문에
JVM
은 String
에 대해 특별한 처 리 를 했 고 문자열 의 상수 를 직접 사용 할 때 클래스 에 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
은 클래스 로 딩 의 핵심 구성 요소 로 모든 Class
은 ClassLoader
으로 로드 되 고 ClassLoader
은 다양한 방식 으로 Class
정보의 바 이 너 리 데이터 흐름 을 시스템 에 읽 어 들 인 다음 JVM
에 연결,초기 화 등 작업 을 한다.따라서 ClassLoader
담당 클래스 의 로드 프로 세 스 는 ClassLoader
을 통 해 클래스 의 연결 과 초기 화 행 위 를 변경 할 수 없습니다.ClassLoader
은 추상 적 인 유형 으로 중요 한 인터페이스 정의 로드 절차 와 로드 방식 을 제공 했다.주요 한 방법 은 다음 과 같다.public Class<?> loadClass(String name) throws ClassNotFoundException
:클래스 이름 을 지정 하고 클래스 를 불 러 옵 니 다.이 클래스 의 Class
인 스 턴 스 를 되 돌려 줍 니 다.이상 을 찾 을 수 없습니다.protected final Class<?> defineClass(byte[] b, int off, int len)
:주어진 바이트 흐름 에 따라 하나의 클래스 를 정의 합 니 다.off
과 len
은 바이트 배열 의 오프셋 과 길 이 를 표시 합 니 다.이것 은 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 류 로 딩 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
IntelliJ IDEA 2019.2의 새로운 기능 프로파일 러를 사용해 보았습니다.이번의 새로운 기능은 초호화! 저는 퍼포먼스 개선을 자주 합니다만, IntelliJ IDEA에도 프로파일링 툴이 붙게 되었으므로, 사용해 보았습니다. 편리한 것 같았기 때문에, 앞으로의 메인 웨폰은 이것이 될 것 같...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.