Java

[JVM 밑바닥까지 파헤치기] 8-1. 바이트코드 실행 엔진: 런타임

noahkim_ 2024. 12. 24. 01:56

저우즈밍 님의 "JVM 밑바닥까지 파헤치기" 책을 정리한 포스팅 입니다

 

  • 가상머신의 실행 엔진은 순수하게 소프트웨어로만 구현됨
  • 명령어 집합의 구조와 실행 엔진을 물리적 제약 없이 원하는 대로 만들 수 있음

 

1. 런타임 스택 프레임 구조

메서드

  • 가장 기본적인 실행 단위

 

스택 프레임

  • 메서드 호출과 실행을 뒷받침하는 내부 데이터 구조
    • 필요한 깊이에 맞게 계산되어 크기가 정해짐
  • 실행중인 메서드의 스택 프레임은 맨 위에 있음

 

지역 변수 테이블
  • 매개 변수와 메서드 안에서 정의된 지역변수 저장
  • 하나의 변수 슬롯 범위로 변수 하나가 할당될 수 있음
  • 인덱스 방식
    • 0: this
    • 매개 변수들
    • 지역 변수들

 

피연산자 스택
  • 타입 크기 단위: 32비트
  • 스택 프레임간 공유
    • 상부의 지역 변수 테이블이 하부의 피연산자 스택과 공유됨
    • 성능 효율 목적

 

동적 링크
  • 메서드에서 이용하는 외부 객체를 가리키는 참조
  • 런타임 상수 풀에 담겨 있음
  • 실행중에 심벌 참조가 직접 참조로 변환됨

 

반환 주소
  • 메서드가 종료되면 호출한 위치로 되돌아가야 함
  • 반환 주소를 프로그램 카운터 값으로 사용함

 

2. 메서드 호출

정적 해석

  • 호출할 메서드의 버전을 선택하는 단계
  • 컴파일 타임에 심벌 참조가 직접 참조로 변경됨
  • 가상 메서드가 해당됨
    • static, final, private, parent, constructor 메서드

 

바이트코드 명령어

  • invokestatic, invokeinterface, invokedynamic
  • invokespecial: constructor, private, parent method
  • invokevirtual: 가상 메서드

 

디스패치

정적 디스패치
  • 컴파일 타임에 참조 타입을 기준으로 메서드 호출이 결정되는 방식 (다중 디스패치)
    • 다형성 구현 불가
  • 메서드 오버로딩

 

동적 디스패치
  • 런타임에 수신 객체의 실제 타입을 기준으로 메서드 호출이 결정되는 방식 (단일 디스패치)
  • 메서드 오버라이딩
  • invokevirtual
    • 후보 메서드들을 찾아 그 중 하나를 특정
    • 실제 타입을 보고 메서드 버전을 선택
  • 메서드 테이블
    • 메서드 정보를 저장 (클래스 로딩 시점)
    • 동적 디스패치를 효율적으로 수행하기 위해 사용됨

 

3. 동적 타입 언어

  • 타입 검사 과정이 런타임에 수행됨
    • 변수 타입은 값이 할당될 때 결정됨 (런타임)
    • 변수 값에 타입이 있음
  • 명확성과 간결성이 뛰어남
  • 성능이 떨어짐

 

런타임 다형성

  • 자바와 같은 정적 타입 언어에서도 런타임 다형성 지원함 

 

메서드 오버라이딩
  • 컴파일 타임: 참조 변수로 호출 가능한 메서드 집합 선택
  • 런타임: 실제 객체 타입에 따라 호출할 메서드 결정 (invokevirtual)

 

동적 타이핑

  • 런타임의 실제 타입에 따라 행동이 결정됨

 

java.lang.invoke 패키지
  • 심벌 참조만으로 호출 대상 메서드를 결정
  • MethodHandle
    • 특정 메서드를 참조
    • 함수 포인터나 위임 메서드 같이 동작
    • 메서드 실행과 관련한 정보만 담김 (리플렉션보다 훨씬 가벼움)
  • MethodType
    • 메서드의 파라미터 타입과 반환 타입 정의
  • CallSite
    • 런타임에서 메서드 호출 지점을 결정

 

invokedynamic
  • 동적 메서드 호출을 지원하는 명령어 (jvm에서 구동되는 동적 타입 언어)
  • 매서드 해석 책임을 사용자 코드로
    • java.lang.invoke 객체로 구현
  • 예시
    • 람다 표현식, 프록시 객체 생성