Java VM 컴파일
<index> <opcode> [<operand1> [<operand2>...]] [<comment>]
각 행은 런타임 상수 풀 인덱스 작업 수를 나타내기 전에 "#"로 시작합니다.
10 ldc #1 // Push float constant 100.0
상량, 국부 변수의 사용과 제어 구조
void spin() {
int i;
for (i = 0; i < 100; i++) {
; // Loop body is empty
}
}
컴파일된 코드:Method void spin()
0 iconst_0 // Push int constant 0
1 istore_1 // Store into local variable 1 (i=0)
2 goto 8 // First time through don’t increment
5 iinc 1 1 // Increment local variable 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
14 return // Return void when done
Java 가상기는 창고 구조를 바탕으로 설계된 것으로 대부분의 조작은 현재 창고 프레임의 조작수 창고에서 1개 이상의 조작수를 꺼내거나 결과를 창고에 눌러 넣는 것이다.모든 방법이 호출되면 새 창고 프레임을 만들고 방법에 필요한 조작 창고와 국부 변수표를 만들 것입니다.모든 스레드가 실행될 때 임의의 시간에 서로 다른 방법으로 호출되어 발생하는 창고 프레임을 포함할 수 있으며, 물론 창고 프레임 내부의 조작 창고도 포함한다.Java 가상 시스템은 명령 iconst <와 같은 동작 코드를 사용하여 동작 수를 암시적으로 포함합니다.i>의 i는 Int 유형 상수 -1, 0, 1, 2, 3, 4, 5를 나타냅니다.이렇게 iconst0은(는) 인바운드 작업에 대한 즉각적인 작업 수 값을 저장할 필요가 없습니다.
명령어 조작의 값은 조작 스택에서 출고된 값이지 국부 변수 자체를 조작하는 것이 아니기 때문에 JVM이 컴파일한 코드에서 국부 변수 테이블과 조작 스택 사이에서 값을 전송하는 명령은 흔히 볼 수 있다.만약에 국부 변수를 사용하고 다시 사용한다면 컴파일러가 결정합니다. 특히load와store 명령에 대해 컴파일러는 국부 변수표를 가능한 한 다시 사용합니다. 이렇게 하면 코드가 효율적이고 간결하며 차지하는 메모리가 적습니다.일부 부분 변수의 빈번한 조작은 JVM에서도 지원되며 iinc 명령은 부분 변수에 1바이트 길이의 기호가 있는 증가량을 추가합니다.반복 구현:
5 iinc 1 1 // Increment local 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
산술 연산 자바 가상기는 조작 창고를 바탕으로 산술 연산을 한다(iinc 명령을 제외하고 국부 변수에 대해 직접 증가 조작을 한다).
int align2grain(int i, int grain) {
return ((i + grain-1) & ~(grain-1));
}
산술 연산에 사용된 조작수는 모두 조작수 창고에서 튀어나왔고 연산 결과는 조작수 창고로 압송되었다.내부 연산을 할 때 중간 연산도 조작수로 사용할 수 있다.예~(grain-1)iload_2 // Push grain
iconst_1 // Push int constant 1
isub // Subtract; push result
iconst_m1 // Push int constant −1
ixor // Do XOR; push result
액세스 실행 시 상수 많은 수치 상수, 대상, 필드, 방법은 현재 클래스의 실행 시 상수 탱크를 통해 접근합니다.int,long,float,double 데이터,string 실례의 인용 형식 데이터에 대한 접근은ldc,ldcw,ldc_w 명령 실현.
ldc와 ldcl 명령어는string 실례를 포함하지만 더블과long 형식의 값은 포함하지 않습니다.실행 시 풀의 항목이 256 (한 바이트 표시 범위) 보다 많을 때ldcw 명령은ldc 명령을 대체하여 상수 탱크에 접근합니다.ldc2_w 액세스 유형이 double 및 Long인 런타임 풀byte,char,short,Int와 같은 정수 상수는 코드에 컴파일되고bipush,sipush,iconst 명령어에 액세스하여 일부 부동 소수점 상수도 fconst <로 컴파일할 수 있습니다.f> 및 dconst
더 많은 제어 구조는 자바 언어에 많은 다른 제어 구조(if-then-else,do,while,break,continue)도 특정한 컴파일 규칙이 있다.
while 문장의 조건 판단(if cmplt 명령으로 이루어짐)은 컴파일 코드에서 순환하는 맨 밑에 있습니다.가상 머신은 각종 데이터 유형의 제어 구조에 대해 비슷한 방식으로 컴파일했고 데이터 유형에 따라 서로 다른 지령을 사용하여 접근했다.
void whileInt() {
int i = 0;
while (i < 100) {
i++;
}
}
//
Method void whileInt()
0 iconst_0
1 istore_1
2 goto 8
5 iinc 1 1
8 iload_1
9 bipush 100
11 if_icmplt 5//
14 return
수신 매개 변수가 n개의 매개 변수를 실례적인 방법에 전달하면 현재 창고 프레임은 약속된 순서에 따라 이 매개 변수를 수신하여 방법의 첫 번째 부분적인 변수 테이블에 저장합니다.실례 방법은 자신의 실례 인용을 0번째 국부 변수로 전달해야 하고static 방법은 실례 인용을 전달할 필요가 없기 때문에this를 저장하기 위해 0번째 국부 변수표를 사용할 필요가 없다.
int addTwo(int i, int j) {
return i + j;
}
//
Method int addTwo(int,int)
0 iload_1 // Push value of local variable 1 (i)
1 iload_2 // Push value of local variable 2 (j)
2 iadd // Add; leave int result on operand stack
3 ireturn // Return int result
방법 호출
일반적인 실례적인 방법에 대한 호출은 실행할 때 대상 유형에 따라 분배된다. invokevirtual 명령을 통해 이루어진다. 모든 invokevirtual 명령은 색인을 나타내는 파라미터를 가지고 있으며 실행 시 이 색인에 있는 항목은 특정한 방법의 기호 인용이다. 이 기호 인용은 방법이 있는 대상의 유형의 내부 이진 이름, 방법 이름과 방법 설명을 제공할 수 있다.
int add12and13() {
return addTwo(12, 13);
}
//
Method int add12and13()
0 aload_0 // Push local variable 0 (this)
1 bipush 12 // Push int constant 12
3 bipush 13 // Push int constant 13
5 invokevirtual #4 // Method Example.addtwo(II)I
8 ireturn
방법 호출 과정: 첫째, 현재 실례 자체 인용을 작업 창고에 압축합니다.둘째, 전달 방법의 매개 변수 값, int값 12와 13을 창고에 넣고 addTwo 방법을 호출할 때 JVM은 마음의 창고 프레임을 만들고 addTwo 방법의 매개 변수를 마음의 창고 프레임에 대응하는 국부 변수의 초기 값으로 전달한다.셋째,addTwo 방법이 실행되고 방법이 되돌아올 때 되돌아오는 값은 호출자(add12and13 방법)의 창고 프레임에 눌려 있는 조작 창고에 들어간다.넷째,add12and13 방법의 반환 과정은add12and13()에서ireturn 명령으로 이루어진다.ireturn 명령은 현재 작업 창고의 창고 상단값을 add12and13 방법을 호출하는 작업 창고에 눌러서 호출자 방법의 다음 명령으로 옮겨서 계속 실행합니다.vokevirtual 명령 조작수 (실행할 때 상수도 탱크 인덱스 #4) 는class 실례의 방법 명령의 편이량이 아닙니다. 컴파일러는class 실례의 내부 구조를 이해할 필요가 없습니다. 방법의 기호를 만들어서 실행할 때 상수도 탱크를 저장하면 됩니다.
클래스 인스턴스를 사용하여 JVM에서 구조 함수는 컴파일러가 제공하는
Object create() {
return new Object();
}
Method java.lang.Object create()
0 new #1 // Class java.lang.Object
3 dup
4 invokespecial #4 // Method java.lang.Object.<init>()V
7 areturn
매개 변수 전달과 방법이 되돌아올 때, 클래스 실례는 일반적인 수치 형식과 크게 다르지 않으며,reference 형식에도 전용 명령이 있습니다.클래스 실례의 필드는 getfield와putfield 명령으로 접근합니다.방법 호출 명령의 조작수든putfield든 getfield 명령의 조작수는 클래스 실례의 주소 편이량이 아닙니다.컴파일러는 이 필드들을 기호 인용으로 생성하여 실행 시간 탱크에 저장하고 실행 시간 상수 탱크는 해석 단계에서 대상의 실제 필드 위치로 변환합니다.
배열은 JVM에서도 객체로 표시되며 배열은 특수 명령 세트에서 작성되고 작동합니다.새 array 명령은 수치 형식의 그룹을 만듭니다.anewarray 명령은 인용 형식의 1차원 그룹을 만듭니다.multianewarray 명령은 한 번에 다차원 그룹을 만듭니다.
//
7 newarray int // ...and create new array of int of that length
//
Method void createThreadArray()
4 anewarray class #1 // Create new array of class Thread
//
Method int create3DArray()[][][]
3 multianewarray #1 dim #2 // Class [[[I, a three
multianewarray 명령의 첫 번째 조작수는 실행할 때 상수 탱크 인덱스입니다. 이것은 그룹을 만들 구성원 형식을 표시합니다.두 번째 조작수는 만들어야 할 실제 비트입니다.모든 수조는 그와 관련된 길이 속성을 가지고 있으며,arraylength 명령을 통해 접근합니다.
switch 문 컴파일
컴파일러는tableswitch와lookupswith 명령을 사용하여 swithc 문장의 컴파일 코드를 생성합니다.switch 명령은 switch 구조의case 문장 블록을 표시합니다. 색인표에서case 문장의 지점 편이량을 효율적으로 확정할 수 있으며, 색인표에서case 문장 블록을 확정할 수 없을 때default 지점이 작용합니다.
int chooseNear(int i) {
switch (i) {
case 0: return 0;
case 1: return 1;
case 2: return 2;
default: return -1;
}
}
Method int chooseNear(int)
0 iload_1 // Push local variable 1 (argument i)
1 tableswitch 0 to 2: // Valid indices are 0 through 2
0: 28 // If i is 0, continue at 28
1: 30 // If i is 1, continue at 30
2: 32 // If i is 2, continue at 32
default:34 // Otherwise, continue at 34
28 iconst_0 // i was 0; push int constant 0...
29 ireturn // ...and return it
30 iconst_1 // i was 1; push int constant 1...
31 ireturn // ...and return it
32 iconst_2 // i was 2; push int constant 2...
33 ireturn // ...and return it
34 iconst_m1 // otherwise push int constant –1...
35 ireturn // ...and return it
tableswitch와lookupswitch 명령은 int 형식의 조건값만 지원하고 다른 수치 형식의 조건값을 사용하면 int 형식의 조건값으로 변환해야 합니다.switch 문장의case 지점 조건값이 비교적 희소할 때tableswitch 명령의 공간 이용률이 비교적 낮을 때lookupswitch 명령으로 대체한다.lookupswitch 명령의 색인표는 int형 키 값과 대응하는 목표 문장의 편이량으로 구성되어 있습니다.lookupswitch 명령이 실행될 때 switch 문장의 조건값과 색인표의 키를 비교합니다.
int chooseFar(int i) {
switch (i) {
case -100: return -1;
case 0: return 0;
case 100: return 1;
default: return -1;
}
}
Method int chooseFar(int)
0 iload_1
1 lookupswitch 3:
−100: 36
0: 38
100: 40
default:42
36 iconst_m1
37 ireturn
38 iconst_0
39 ireturn
40 iconst_1
41 ireturn
42 iconst_m1
43 ireturn
자바 가상기에서 규정한lookupswitch 명령의 색인표는 키 값에 따라 정렬해야 한다. 이렇게 사용하면 직접 선형 스캐닝 검색을 사용하는 것보다 효율적이다.색인표에서 분기 편이량을 확정하는 과정에서lookupswitch 명령은 조건값을 서로 다른 키와 비교하고tableswitch 명령은 색인으로 한 번의 범위 검사만 하면 된다.작업 스택을 사용하는 JVM은 작업 스택을 쉽게 사용할 수 있도록 작업 스택 데이터 유형을 구분하지 않는 많은 명령을 제공합니다.
public long nextIndex() {
return index++;
}
private long index = 0;
Method long nextIndex()
0 aload_0 // Push this
1 dup // Make a copy of it
2 getfield #4 // One of the copies of this is consumed
// pushing long field index,above the original this
5 dup2_x1 // The long on top of the operand stack is
// inserted into the operand stack below theo riginal this
6 lconst_1 // Push long constant 1
7 ladd // The index value is incremented...
8 putfield #4 // ...and the result stored back in the field
11 lreturn // The original value of index is left on top of the operand stack, ready to be returned
JVM에서는 작업 스택에 적용되는 명령을 수정하거나 분할할 수 없습니다.상례에서 복사를 사용했고 롱 형식의 동작은 lconst 에 대응합니다1등은 두 동작으로 나누어지지 않았다.
throw 키워드를 사용한 버그 제거 및 처리 예외 프로그램은 다음과 같이 컴파일됩니다.
void cantBeZero(int i) throws TestExc {
if (i == 0) {
throw new TestExc();
}
}
Method void cantBeZero(int)
0 iload_1 // Push argument 1 (i)
1 ifne 12 // If i==0, allocate instance and throw
4 new #1 // Create instance of TestExc
7 dup // One reference goes to the constructor
8 invokespecial #7 // Method TestExc.<init>()V
11 athrow //
12 return // ,
try-catch 구조:
void catchOne() {
try {
tryItOut();
} catch (TestExc e) {
handleExc(e);
}
}
Method void catchOne()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 return //try block
5 astore_1 // Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #5 //
// Example.handleExc(LTestExc;)V
11 return // Return after handling TestExc
Exception table:// ,from~to , TestExec, 5
From To Target Type
0 4 5 Class TestExc
이상 시계는 여러 개의 이상이 있을 수 있다.
void catchTwo() {
try {
tryItOut();
} catch (TestExc1 e) {
handleExc(e);
} catch (TestExc2 e) {
handleExc(e);
}
}
// ,
Method void catchTwo()
Exception table:
From To Target Type
0 4 5 Class TestExc1
0 4 12 Class TestExc2
void nestedCatch() {
try {
try {
tryItOut();
} catch (TestExc1 e) {
handleExc1(e);
}
} catch (TestExc2 e) {
handleExc2(e);
}
}
xception table:
From To Target Type
0 4 5 Class TestExc1
0 12 23 Class TestExc1
finally 문장 블록try-finally와try-catch 문장은 기본적으로 같다.코드가try문장을 실행하기 전에 이상이 있든 없든finally문장 블록의 코드가 실행됩니다.jsr 명령어로finally 문장 블록을 컴파일합니다.
void tryFinally() {
try {
tryItOut();
} finally {
wrapItUp();
}
}
Method void tryFinally()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 jsr 14 // Call finally block
7 return // End of try block
8 astore_1 // Beginning of handler for any throw
9 jsr 14 // Call finally block
12 aload_1 // Push thrown value
13 athrow // ...and rethrow the value to the invoker
14 astore_2 // Beginning of finally block
15 aload_0 // Push this
16 invokevirtual #5 // Method Example.wrapItUp()V
19 ret 2 // Return from finally block
Exception table:
From To Target Type
0 4 8 any
try 문장을 종료하는 네 가지 방법: 1) 문장이 정상적으로 실행되고 끝납니다.2)return 문장을 통해 종료 3)break 또는continue를 통해 종료 순환 4) 이상 던지기
동기화 동기화는 Moniter의 진입과 종료 대상을 바탕으로 이루어진다. 현식 동기화든 은식 동기화든 마찬가지다.
void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}
Method void onlyMe(Foo)
0 aload_1 // Push f
1 dup // Duplicate it on the stack
2 astore_2 // Store duplicate in local variable 2
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual #5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 goto 18 // Complete the method normally
13 astore_3 // In case of any throw, end up here
14 aload_2 // Push local variable 2 (f)
15 monitorexit // Be sure to exit the monitor!
16 aload_3 // Push thrown exception...
17 athrow // ...then rethrow the value to the invoker
18 return // Return in the normal case
Exception table:
From To Target Type
4 10 13 any
13 16 13 any
컴파일러서는 방법이 어떤 방식으로 완성되든지, 방법 호출 과정 중의 모든 모니터 명령은 대응하는 모니터 xit 명령을 실행해야 한다. 이 방법이 정상적으로 끝나든지 비정상적으로 끝나든지.방법이 이상할 때 모니터와 모니터 exit 명령을 정확하게 맞출 수 있도록 컴파일러는 자동으로 이상 프로세서를 생성하여 모든 이상을 처리합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.