Java 클래스 로드 전 과정 깊이 이해
자바 파일이 불러오기부터 제거되기까지 총 4단계를 거쳐야 합니다.
로드 -> 링크 (검증 + 준비 + 확인) -> 초기화 (사용 전 준비) -> 사용 -> 마운트 해제
그 중에서 불러오기(사용자 정의 불러오기 제외) + 링크의 과정은 완전히 jvm가 책임지고 클래스를 초기화하는 작업(불러오기 + 링크는 그 전에 이미 완성), jvm는 엄격한 규정(4가지 상황)이 있습니다.
1. new, getstatic,putstatic,invokestatic 네 바이트 코드 명령을 만났을 때 클래스를 초기화하지 않으면 바로 초기화 작업을 합니다.사실은 세 가지 상황이다. new로 하나의 클래스를 실례화할 때, 클래스의 정적 필드를 읽거나 설정할 때, (final에 의해 수식된 정적 필드를 포함하지 않는다. 왜냐하면 그들은 이미 상량 탱크에 들어갔기 때문이다), 그리고 정적 방법을 실행할 때.
2. 자바를 사용한다.lang.reflect.*클래스에 대한 반사 호출을 할 때 클래스가 초기화되지 않았다면 바로 클래스를 진행합니다.
3. 한 종류를 초기화할 때 아버지가 초기화되지 않으면 먼저 아버지를 초기화한다.
4. jvm가 시작될 때 사용자가 실행할 메인 클래스 (staticvoidmain (String []args) 를 포함하는 클래스) 를 지정해야 하면 jvm가 먼저 이 클래스를 초기화합니다.
상기 4가지 예처리는 한 클래스에 대한 주동적인 인용이라고 하고 나머지 다른 상황은 수동적인 인용이라고 하며 클래스의 초기화를 촉발하지 않는다.다음은 수동 참조의 예입니다.
/**
* 1
* ,
* @author volador
*
*/
class SuperClass{
static{
System.out.println("super class init.");
}
public static int value=123;
}
class SubClass extends SuperClass{
static{
System.out.println("sub class init.");
}
}
public class test{
public static void main(String[]args){
System.out.println(SubClass.value);
}
}
출력 결과: 슈퍼 클래스 init.
/**
* 2
* ,
* @author volador
*
*/
public class test{
public static void main(String[] args){
SuperClass s_list=new SuperClass[10];
}
}
출력 결과: 출력 없음
/**
* 3
* , ,
* @author root
*
*/
class ConstClass{
static{
System.out.println("ConstClass init.");
}
public final static String value="hello";
}
public class test{
public static void main(String[] args){
System.out.println(ConstClass.value);
}
}
출력 결과:hello(tip:컴파일할 때,ConstClass.value는hello 상수로 바뀌어test류의 상수 탱크에 넣었습니다)이상은 클래스의 초기화입니다. 인터페이스도 초기화해야 합니다. 인터페이스의 초기화는 클래스의 초기화와 약간 다릅니다.
위의 코드는 모두 static {}로 초기화 정보를 출력합니다. 인터페이스는 할 수 없지만 인터페이스가 초기화될 때 컴파일러는 인터페이스에
다음 클래스의 불러오는 전 과정을 분해합니다: 불러오기->검증->준비->해석->초기화
먼저 로드:
이 가상 시스템은 다음과 같은 3가지 작업을 수행합니다.
1.클래스의 모든 한정된 이름을 통해 이러한 바이트 흐름을 정의합니다.
2.이 바이트 흐름을 대표하는 정적 저장 구조를 방법 구역의 운행 시 데이터 구조로 전환합니다.
3.자바 더미에서 이 종류를 대표하는 자바를 생성합니다.이 데이터에 대한 접근 입구로 lang.Class 대상입니다.
첫 번째는 매우 유연하다. 많은 기술들이 이곳에서 삽입된다. 왜냐하면 이진 흐름이 어디에서 오는지 한정하지 않기 때문이다.
class 파일에서 -> 일반 파일 불러오기
zip 패키지에서 ->jar의 클래스 불러오기
네트워크에서 -> Applet
..........
탑재 과정의 다른 몇 가지 단계에 비해 탑재 단계의 제어성이 가장 강하다. 왜냐하면 유형의 탑재기는 시스템적으로도 쓸 수 있고 스스로 쓸 수도 있기 때문이다. 프로그램원은 자신의 방식으로 탑재기를 써서 바이트 흐름의 획득을 제어할 수 있기 때문이다.
2진 흐름을 가져오면 jvm에 필요한 방식으로 방법 구역에 저장되며, 자바 더미에 자바를 실례화합니다.lang. Class 대상은 무더기의 데이터와 연결됩니다.
로드가 완료되면 바이트 흐름을 검사하기 시작합니다. (사실은 파일 형식 검증과 같은 여러 절차가 교차되어 진행됩니다.)
검사의 목적:class 파일의 바이트 흐름 정보가 jvm의 입맛에 부합되고 jvm가 불편하지 않도록 확보합니다.만약class 파일이 순수한java 코드로 컴파일된다면 수조가 경계를 넘어 존재하지 않는 코드 블록으로 이동하는 것과 같은 불건전한 문제가 발생하지 않을 것이다. 왜냐하면 이런 현상이 발생하면 컴파일러가 컴파일을 거절하기 때문이다.그러나 앞서 말한 바와 같이 Class 파일 흐름은 반드시 자바 원본에서 컴파일된 것이 아니라 인터넷이나 다른 곳에서 왔을 수도 있다. 심지어 16진법으로 작성할 수도 있다. 만약에 jvm가 이 데이터를 검사하지 않는다면 유해한 바이트 흐름이 jvm를 완전히 붕괴시킬 수도 있다.
검증은 주로 몇 가지 절차를 거친다. 파일 형식 검증 -> 메타데이터 검증 -> 바이트 코드 검증 -> 기호 인용 검증
파일 형식 검증: 바이트 흐름이 Class 파일 형식의 규범에 부합되는지 확인하고 현재 jvm 버전에서 처리될 수 있는지 검증합니다.ok 문제가 없으면 바이트 흐름은 메모리의 방법 구역에 들어가서 저장할 수 있습니다.뒤의 세 가지 검사는 모두 방법 구역에서 진행되었다.
메타데이터 검증: 바이트 코드 묘사 정보에 대해 의미화 분석을 하고 그 묘사 내용이 자바 언어의 문법 규범에 부합하도록 확보한다.
바이트 코드 검사: 가장 복잡하고 상대방 법체의 내용을 검사하여 운행할 때 격식에 어긋나는 일을 하지 않도록 보증한다.
기호 인용 검증: 일부 인용의 진실성과 타당성을 검증한다. 예를 들어 코드에서 다른 종류를 인용했다. 여기서 그것이 도대체 존재하는지 확인해야 한다.또는 코드에서 다른 종류의 일부 속성에 접근했다면, 여기서 그 속성에 접근할 수 있는 줄을 검증했다.(이 단계는 뒤의 해석 작업에 기반을 다질 것이다)
검증 단계는 매우 중요하지만 필요한 것도 아니다. 만약에 일부 코드가 반복적으로 사용되고 신뢰성을 검증했다면 실시 단계는 - Xverify:none 매개 변수로 대부분의 클래스 검증 조치를 닫고 클래스 불러오는 시간을 짧게 할 수 있다.
이어서 위의 절차를 완성하면 준비 단계에 들어갈 것이다.
이 단계는 클래스 변수(정적 변수를 가리키는 말)에 메모리를 분배하고 클래스의 초기 값을 설정하는 단계로 이 안에 존재하는 방법 구역에서 분배됩니다.여기서 설명하자면, 이 단계는 정적 변수에 초기 값만 설정하고, 그 실례 변수는 실례화 대상에서 분배된다.여기에 주어진 클래스 변수의 초기 값은 클래스 변수의 부여 값과 약간 다르다. 예를 들어 다음과 같다.
public static int value=123;
이 단계에서value의 값은 123이 아니라 0이 될 것입니다. 이때는 자바 코드를 실행하기 시작하지 않았기 때문에 123은 보이지 않습니다. 123을 value에 부여하는putstatic 명령은 프로그램이 컴파일된 후에 여기에도 예외가 있다.
public static final int value=123;
여기 준비 단계에서value의 값은 123으로 초기화됩니다.이것은 컴파일러에서javac는 이 특수한value를 위해ConstantValue 속성을 생성하고 준비 단계에서 jm는 이ConstantValue의 값에 따라value에 값을 부여한다는 것이다.상단을 완성한 후에 분석을 진행해야 한다.해석은 클래스의 필드, 방법 등을 바꾸는 것 같지만 클래스 파일의 형식 내용과 구체적으로 관련되어 깊이 이해하지 못했다.
초기화 프로세스는 클래스 로딩 프로세스의 마지막 단계입니다.
앞의 클래스 마운트 과정에서 마운트 단계에서 사용자가 사용자 정의 클래스 마운트를 통해 참여할 수 있는 것을 제외하고 다른 동작은 jvm가 주도하고 이 부분을 초기화해야 자바 안의 코드를 진정으로 실행하기 시작한다.
이 단계는 일부 사전 조작을 실행할 것입니다. 준비 단계를 구분하고 클래스 변수에 대해 시스템에 값을 부여했습니다.
사실 말하자면, 이 단계는 실행 프로그램의
다음은 예를 들어 설명합니다.
static class Parent{
public static int A=1;
static{
A=2;
}
}
static class Sub extends Parent{
public static int B=A;
}
public static void main(String[] args){
System.out.println(Sub.B);
}
우선 Sub.B에서 정적 데이터를 참조하고 Sub 클래스를 초기화합니다.또한 상위 Parent는 초기화 작업을 먼저 수행합니다.Parent 초기화 후, A=2, 그래서 B=2;이전 절차는 다음과 같습니다.
static class Parent{
<clinit>(){
public static int A=1;
static{
A=2;
}
}
}
static class Sub extends Parent{
<clinit>(){ //jvm
public static int B=A;
}
}
public static void main(String[] args){
System.out.println(Sub.B);
}
인터페이스 안에 static {} 이런 정적 코드 블록이 존재할 수 없지만 변수가 초기화될 때의 변수 부여 작업이 존재할 수 있기 때문에 인터페이스 안에도
또한 인터페이스의 실현 클래스는 초기화할 때도 인터페이스의
또한 jvm는 하나의 종류의
다음은 예를 들어 설명합니다.
public class DeadLoopClass {
static{
if(true){
System.out.println(" ["+Thread.currentThread()+"] , ");
while(treu){}
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("toplaile");
Runnable run=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("["+Thread.currentThread()+"] ");
DeadLoopClass d=new DeadLoopClass();
System.out.println("["+Thread.currentThread()+"] ");
}};
new Thread(run).start();
new Thread(run).start();
}
}
이 안에서 운행할 때 막히는 현상을 볼 수 있다.읽어주셔서 감사합니다. 여러분에게 도움이 되었으면 좋겠습니다. 본 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.