JVM(Java Virtual Machine) 명령어는 특정 의미를 나타내는 바이트 길이 연산 코드(Opcode)와 그 뒤에 연산 매개변수를 나타내는 0개 이상의 피연산자로 구성됩니다. 가상 머신의 많은 명령어에는 피연산자가 없고 opcode만 포함되어 있습니다. 예외가 무시되면 JVM 인터프리터는 단 하나의 코드로 효과적으로 작동할 수 있습니다.
다음과 같이 코드 코드를 복사합니다 .
하다{
PC 레지스터를 자동으로 계산하고 PC 레지스터 위치에서 opcode를 검색합니다.
if(피연산자가 존재함) 피연산자를 제거합니다.
opcode에 의해 정의된 작업을 수행합니다.
}while(다음 루프 처리)
피연산자의 수와 길이는 opcode에 따라 다릅니다. 피연산자의 길이가 1바이트를 초과하면 Big-Endian 순서(상위 첫 번째 바이트코드)로 저장되며 해당 값은 (byte1<<8) | 바이트2.
바이트코드 명령어 스트림은 "tableswitch" 및 "lookupswitch" 명령어를 제외하고 단일 바이트로 정렬됩니다. 해당 피연산자는 특수하며 정렬을 위해 해당 간격을 예약해야 합니다.
JVM(Java Virtual Machine) opcode의 길이를 1바이트로 제한하고 컴파일된 코드의 매개변수 길이 정렬을 포기하는 것은 JVM 구현에 특정 성능 비용이 들더라도 짧고 간결한 컴파일된 코드를 얻는 것입니다. Opcode는 길이가 1바이트만 가능하므로 명령어 세트 수를 제한하고 데이터가 잘 정렬되어 있다고 가정하지 않습니다. 즉, 데이터가 1바이트를 초과하면 특정 데이터 구조를 다음에서 재구성해야 합니다. 일부 성능이 손실됩니다.
데이터 유형 및 Java Virtual Machine
JVM의 명령어 세트에서 대부분의 명령어에는 해당 작업에 해당하는 데이터 유형 정보가 포함됩니다. 예를 들어, iload 명령어는 지역 변수 테이블의 int 유형 데이터를 피연산자 스택으로 로드하는 반면 fload는 float 유형 데이터를 로드합니다.
데이터 유형과 관련된 대부분의 바이트코드 명령어의 경우 opcode 니모닉에는 다음을 나타내는 특수 문자가 있습니다. i는 int 유형, l은 long, s는 short, b는 바이트, c는 char, f는 float, d는 double을 나타냅니다. a는 참조를 의미합니다. 필요한 경우 지원되지 않는 유형을 지원되는 유형으로 변환하는 데 사용할 수 있는 별도의 지시문이 있습니다.
로드 및 저장 지침
로드 및 저장 명령어는 스택 프레임의 지역 변수 테이블과 피연산자 스택 간에 데이터를 전송하는 데 사용됩니다.
1) 지역 변수를 피연산자 스택에 로드하는 명령에는 iload, iload_<n>, lload, lload_<n>, float, fload_<n>, dload, dload_<n>, aload, aload_<n>이 포함됩니다.
2) 피연산자 스택의 값을 로컬 변수에 저장하기 위한 지침: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) 피연산자 스택에 상수를 로드하기 위한 지침: bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, Iconst_ml, Iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>
4) 지역변수 테이블의 접근 인덱스 명령: 와이드
꺾쇠 괄호로 끝나는 일부 명령어는 iload_0, iload_1 등을 나타내는 iload_<i>와 같은 명령어 그룹을 나타냅니다. 이러한 명령어 그룹은 피연산자가 하나인 일반 명령어입니다.
작동 지침
산술 명령어는 두 피연산자 스택의 값에 대해 특정 연산을 수행하고 결과를 연산 스택의 맨 위에 다시 저장하는 데 사용됩니다.
1) 추가 지침: iadd,ladd,fadd,dadd
2) 뺄셈 명령어: isub, lsub, fsub, dsub
3) 곱셈 명령: imul, lmul, fmul, dmul
4) 나누기 명령어: idiv, ldiv, fdiv, ddiv
5) 나머지 명령어 : irem, lrem, frem, drem
6) 부정 명령: ineg, length, fneg, dneg
7) 변위 지침: ishl,ishr,iushr,lshl,lshr,lusr
8) 비트별 OR 명령어: ior, lor
9) 비트 AND 명령어: iand, land
10) 비트별 XOR 명령어: ixor, lxor
11) 지역변수 증가 명령: iinc
12) 비교 명령어: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
Java 가상 머신은 정수 데이터의 오버플로를 명확하게 규정하지 않지만 정수 데이터를 처리할 때 나누기와 나머지 명령만 사용하면 제수가 0일 때 가상 머신에서 예외가 발생하도록 규정합니다.
로드 및 저장 지침
로드 및 저장 명령어는 스택 프레임의 로컬 변수 테이블과 피연산자 스택 간에 데이터를 전송하는 데 사용됩니다.
1) 지역 변수를 피연산자 스택에 로드하는 명령에는 iload, iload_<n>, lload, lload_<n>, float, fload_<n>, dload, dload_<n>, aload, aload_<n>이 포함됩니다.
2) 피연산자 스택의 값을 로컬 변수에 저장하기 위한 지침: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) 피연산자 스택에 상수를 로드하기 위한 지침: bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, Iconst_ml, Iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>
4) 지역변수 테이블의 접근 인덱스 명령: 와이드
꺾쇠 괄호로 끝나는 일부 명령어는 iload_0, iload_1 등을 나타내는 iload_<i>와 같은 명령어 그룹을 나타냅니다. 이러한 명령어 그룹은 피연산자가 하나인 일반 명령어입니다.
작동 지침
산술 명령어는 두 피연산자 스택의 값에 대해 특정 연산을 수행하고 결과를 연산 스택의 맨 위에 다시 저장하는 데 사용됩니다.
1) 추가 지침: iadd,ladd,fadd,dadd
2) 뺄셈 명령어: isub, lsub, fsub, dsub
3) 곱셈 명령: imul, lmul, fmul, dmul
4) 나누기 명령어: idiv, ldiv, fdiv, ddiv
5) 나머지 명령어 : irem, lrem, frem, drem
6) 부정 명령: ineg, length, fneg, dneg
7) 변위 지침: ishl,ishr,iushr,lshl,lshr,lusr
8) 비트별 OR 명령어: ior, lor
9) 비트 AND 명령어: iand, land
10) 비트별 XOR 명령어: ixor, lxor
11) 지역변수 증가 명령: iinc
12) 비교 명령어: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
Java 가상 머신은 정수 데이터의 오버플로를 명확하게 규정하지 않지만 정수 데이터를 처리할 때 나누기와 나머지 명령만 사용하면 제수가 0일 때 가상 머신에서 예외가 발생하도록 규정합니다.
유형 변환 지침
유형 변환 명령어는 두 개의 JVM(Java Virtual Machine) 숫자 유형을 서로 변환합니다. 이러한 작업은 일반적으로 사용자 코드에서 명시적인 유형 변환 작업을 구현하는 데 사용됩니다.
JVM은 확장된 유형 변환(작은 범위 유형을 큰 범위 유형으로 변환)을 지원합니다.
1) Int 유형에서 long, float, double 유형으로
2) 뜨는 롱타입, 더블타입
3) float에서 double형으로
좁은 유형 변환 지침: i2b, i2c, i2s, l2i, f2i, f2l, d2l 및 d2f 좁은 유형 변환으로 인해 변환 결과가 다른 부호와 크기 순서로 생성될 수 있으며 변환 프로세스로 인해 숫자 값의 정밀도가 떨어질 수 있습니다. 예를 들어 int 또는 long 유형이 정수 유형 T로 변환되면 변환 프로세스에서는 가장 낮은 N 바이트(N은 T 유형의 데이터 유형 길이)의 예상치 못한 내용만 삭제합니다.
객체 생성 및 조작
클래스 인스턴스와 배열은 모두 객체이지만 JVM(Java Virtual Machine)은 클래스 인스턴스와 배열의 생성 및 작동에 대해 서로 다른 바이트코드 명령을 사용합니다.
1) 인스턴스 생성 명령: new
2) 배열 생성 지침: newarray, anewarray, multianewarray
3) 액세스 필드 명령어: getfield, putfield, getstatic, putstatic
4) 피연산자 스택 명령어에 배열 요소 로드: baload, caload, saload, iaload, laload, faload, daload, aaload
5) 피연산자 스택의 값을 배열 요소에 저장하고 다음을 실행합니다: bastore,castore,castore,sastore,iastore,fastore,dastore,aastore
6) 배열 길이 명령어를 가져옵니다: arraylength
7) 인스턴스 유형 확인 지침: instanceof, checkcast
피연산자 스택 관리 지침
피연산자 스택을 직접 조작하는 명령어: pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2 및 swap
제어 전송 명령
JVM이 제어 전송 명령이 아닌 지정된 명령 뒤의 명령에서 조건부로 또는 무조건적으로 프로그램 실행을 계속할 수 있도록 합니다. 제어 전송 지침에는 다음이 포함됩니다.
1) 조건 분기: ifeq, iflt, ifle, ifne, ifgt, ifge, ifnull, ifnotnull, if_cmpeq, if_icmpne, if_icmlt, if_icmpgt 등
2) 복합 조건 분기: tableswitch, lookupswitch
3) 무조건 분기: goto,goto_w,jsr,jsr_w,ret
JVM에는 int 및 참조 유형의 조건부 분기 비교 작업을 처리하기 위한 특수 명령 세트가 있습니다. 엔터티 값이 null인지 여부를 명확하게 표시하기 위해 null 값을 검색하는 특수 명령이 있습니다. boolean형과 byte형, char형, short형의 조건부 분기 비교 연산은 모두 int형 비교 명령어를 사용하여 완료되고, long, float, double 조건부 분기 비교 연산은 해당 타입의 비교 연산 명령어로 수행되며, 명령어는 피연산자 스택에 정수 값을 추가한 다음 int 유형의 조건 비교 연산을 수행하여 전체 분기 점프를 완료합니다. 모든 유형의 비교는 결국 int 유형 비교 작업으로 변환됩니다.
메소드 호출 및 반환 지침
Invokevirtual 명령어: 객체의 인스턴스 메소드를 호출하고 객체의 실제 유형에 따라 디스패치합니다(가상 머신 디스패치).
호출인터페이스 명령: 인터페이스 메서드를 호출하고, 런타임에 이 인터페이스 메서드를 구현하는 객체를 검색하고, 호출할 적절한 메서드를 찾습니다.
Invokespecial: 인스턴스 초기화 메서드, 프라이빗 메서드, 부모 클래스 메서드 등 특별한 처리가 필요한 인스턴스 메서드 호출
Invokestatic: 클래스 메서드 호출(정적)
메소드 반환 명령어는 반환값의 종류에 따라 ireturn(반환값은 boolean, byte, char, short, int), lreturn, freturn, drturn, areturn 등으로 구분된다. , 클래스 및 인터페이스의 클래스 초기화 i 메소드가 사용됩니다.
동기식
JVM은 메소드 수준 동기화와 메소드 내 명령 시퀀스의 동기화를 지원하며 둘 다 모니터를 통해 구현됩니다.
메서드 수준 동기화는 암시적이며 바이트코드 명령을 통해 제어할 필요가 없습니다. 이는 메서드 호출 및 반환 작업에서 구현됩니다. 가상 머신은 메소드 상수 풀의 메소드 표준 구조에 있는 ACC_SYNCHRONIZED 플래그를 통해 동기 메소드인지 여부를 구별합니다. 메서드가 호출되면 호출 명령어는 플래그가 설정되어 있는지 확인합니다. 플래그가 설정되어 있으면 실행 스레드가 모니터를 유지한 다음 메서드를 실행하고 마지막으로 메서드가 완료되면 모니터를 해제합니다.
일반적으로 동기화된 블록으로 표시되는 일련의 명령어 세트를 동기화합니다. JVM 명령어 세트에는 동기화된 의미를 지원하는 monitorenter 및 monitorexit가 있습니다.
구조적 잠금은 메소드 호출 중 각 모니터 종료가 이전 모니터 항목과 일치하는 경우입니다. JVM은 다음 두 가지 규칙을 통해 구조적 잠금이 설정되도록 보장합니다(T는 스레드를 나타내고 M은 모니터를 나타냄).
1) 메소드 실행 중 T가 M을 보유하는 횟수는 메소드가 완료될 때 T가 M을 해제하는 횟수와 동일해야 합니다.
2) T가 M을 보유하는 것보다 T가 M을 더 많이 릴리스하는 상황은 언제든지 발생하지 않습니다.