클래스 파일의 Code Attribute 에 순서대로 나열되는 바이트 코드 명령어는 u1 타입이기에 총 256 개의 명령어를 나타낼 수 있다.
현재는 200개 넘는 수의 바이트 코드 명령어가 있다.
[바이트 코드 명령어] + [필요시 피연산자] 형태로 작성된다.바이트 코드 명령어의 실행을 간단하게 표현하면 아래 4가지의 반복이다.
대다수 명령어는 앞에 타입을 나타내는 문자가 적혀있다. ex) iload(int load), fstore(float store), laload(long array load)
일부 명령어는 머신 내에서 타입 변환하여 쓰인다

https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-7.html
loadiload, ~iload_<n>, lload, ~lload_<n>, fload, ~fload_<n>, dload, ~dload_<n>, aload, ~aload_<n>storeistore, istore_<n>, lstore, lstore_<n>, fstore, fstore_<n>, dstore, dstore_<n>, astore, astore_<n>bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, icont_m1, icont_m1, lcont_<l>, fcont_<f>, dcont_<d>wide_<n> 과 같이 써 있는 것들은 피연산자를 포함하고 있는 명령어들로, 자주 쓰이는 피연산자를 명령어에 합쳤다.
예를 들어 iload_0 은 iload 0 과 같다.
피연산자 두개의 값으로 특정 산술 연산 후 결괏값을 다시 피연산자 스택의 맨 위에 저장한다.
byte, short, char, boolean 은 직접 지원하는 산술 명령어가 없으며, int 명령어로 사용한다.
정수 연산의 오버플로는 명시가 없다
정수 나누기와 나머지 연산시 나누는 값이 0 이면 ArithmeticException 은 규정에 있다
이 외의 다른 정수 산술 시나리오에서는 런타임 예외를 던지지 않아야 한다.
IEEE754 - 부동 소수점
비정규화된 부동 소수점 수, 점진적 언더플로 지원해야함
모든 ㅇ녀산 결과를 적절한 정밀도로 반올림해야함
정확하지 않은 결과는 표현 가능한 가장 가까운 값으로 반올림
표현 가능한 두 값이 수학적으로 정확한 값과 차이가 똑같다면 최하위 비트가 0인 값을 우선시 -> 가까운 값으로 반올림
부동 소수점 수를 정수로 반환할 때는 0에 가까운 값으로 반올림(양수면 내림, 음수면 올림) -> 소수부의 모든 중요 바이트를 버림 - 대상 숫자 타입이 표현할 수 있는 숫자 중 정확한 값과 가장 가까우면서 더 크지 않은 값 선택해줌
부동 소수점 연산을 수행할 때 런타임 예외를 던지지 않는다.
연산이 오버플로 되면 부호있는 무한대로 표현
연산 결과에 대한 명확한 수학적 정의가 없다면 NaN
long 타입 비교시엔 부호 있는 비교 사용
jcmpg, dcmpl, fcmpg, fcmpl 등이 IEEE754 가 정의한 예외(시그널) 을 보내지 않는 비교
숫자 타입 -> 다른 숫자 타입
코드에 명시한 형변환, char 등 전용 연산 코드가 없을때의 자동 현변환
데이터 타입의 표현 범위가 넓어지는 경우는 형 변환 명령어가 없어도 자동 처리됨
표현 범위가 줄어드는 경우 형 변환 명령어가 반드시 필요(i2b, i2c, i2s...)
PC 레지스터 값을 조건적으로 또는 무조건 적으로 다른 명령어로 변환하는 명령어다.
변경 대상 명령어는 제어 전이 명령어가 아니어야 한다.
jsr_w : jump to subroutine wide
ret : return from subroutine
invokevirtual : 인스턴스 메서드 호출. 객체의 실제 타입에 따라 디스패치.invokeinterface : 인터페이스 메서드 호출. 런타임에 인터페이스 구현 객체의 메서드를 찾아 호출.invokespecial : 인스턴스 초기화 메서드, private 메서드, 부모 클래스의 메서드 등invokestatic : static 메서드invokedynamic : 런타임에 callsite 한정자가 참조하는 메서드를 동적으로 찾아 호출. 앞의 네 명령어의 디스패치 로직은 사용자가 변경할 수 없지만, invokedynamic 의 디스패치 로직은 jvm 실행 시 사용자가 설정할 수 있다.호출 대상 결정을 런타임으로 미룬다.
대표적인 예로 람다가 있다. 람다는 컴파일시 invokedynamic 명령어 실행으로 나온다. 호출 대상 클래스가 정해져 있지 않다.
람다의 첫 호출시 컴파일때 정해진 방법으로 BootStrapMethod 를 호출한다. 이 때 어떤 메서드를 실행할지 결정하고 만든다. 따라서 첫 호출시에는 느릴 수 있다.
이후 호출시에는 callsite 라 불리는 미리 기억된 경로로 BSM 에서 생성된 메서드를 빠르게 실행한다.
- 바이트코드에서 invokedynamic 만남
- Bootstrap Method(BSM) 호출 (딱 한 번)
- BSM이 CallSite 반환
- CallSite 안의 MethodHandle 이 실제 호출 대상
- 이후 호출은 직접 jump (거의 invokestatic 급)
dynamicinvoke
동적 언어를 정적 언어 처럼 최적화 가능
public class InvokedynamicTest {
static Runnable makeStatelessLambda() {
return () -> System.out.println("no capture");
}
static Runnable makeStatusLambda(int x) {
return () -> System.out.println("capture x = " + x);
}
public static void main(String[] args) {
Runnable r1 = makeStatelessLambda();
Runnable r2 = makeStatelessLambda();
System.out.println(r1 == r2);
System.out.println(System.identityHashCode(r1));
System.out.println(System.identityHashCode(r2));
Runnable r3 = makeStatusLambda(10);
Runnable r4 = makeStatusLambda(10);
System.out.println(r3 == r4);
System.out.println(System.identityHashCode(r3));
System.out.println(System.identityHashCode(r4));
}
}
throw 구문으로 명시적 예외 발생시 athrow 바이트 코드 명령어가 사용됨monitorenter 를 사용하여 락(모니터 라고도 함) 을 획득한다.monitorexit 은 락은 해제하는 명령어이다.monitorenter 에 대응되는 monitorexit 가 실행되도록 보장한다.