개요
JVM의 메모리 영역 구성 요소중 하나인 자바가상머신 스택에 대한 이해를 위해 예제를 통해 각 구역에 데이터가 할당되고 해제되는것을 javap 명령어를 통해 알아본다.
*해당 포스팅에서 나오는 상수풀은 런타임 상수풀이 아니라 클래스 상수풀이다.
JVM이 클래스 로드시에 클래스 상수풀 내의 파일이 런타임 상수풀에 적재된다.
실습 코드 준비 및 컴파일
public class BytecodeExample {
public static void main(String[] args) {
int a = 5;
int b = 10;
int result = addAndMultiply(a, b);
System.out.println(result);
}
public static int addAndMultiply(int x, int y) {
int sum = x + y;
int product = x * y;
return sum + product;
}
}
로컬 변수 배열, 오퍼랜드(계산) 스택, 메서드 호출/복귀, 상수 풀 에 대해 알아보기 위해 따로 메서드도 만들어 주었고 계산 과정도 추가해주었다.
이후에 컴파일과 javap 옵션으로 바이트 코드를 읽어와보자
BytecodeExample 클래스 기본 생성자 바이트 코드
aload_0 (0)
- 의미: 로컬 변수 0번 인덱스에 있는 값을 오퍼랜드 스택에 푸시
- 클래스의 자신을 가르키는 this 는 만들어진 인스턴스 메서드의 첫번째 인자로 전달되고 로컬 변수 배열의 0번 인덱스에 저장된다.
- 이후의 invokespecial 명령어에서 사용
invokespecial (1)
- 의미: 상위 클래스, 생성자, 또는 private 메서드를 호출, 여기에서는 생성자 호출 후에 객체 초기화를 한다.
- 오퍼랜드 스택에 있는 this 참조를 사용하여 java/lang/Object의 생성자를 호출한다.
- this 참조가 사용되어 상위 클래스 생성자가 호출된다.
return (4)
- 의미: 메서드를 종료하고, 호출한 위치로 제어를 반환한다.
- return 명령어는 현재 실행 중인 메서드나 생성자를 종료하고 오퍼랜드 스택을 비운다.
최종적으로 BytecodeExample의 객체가 생성되고, 모든 초기화 작업이 완료된 상태가 된다.
main 메서드와 addAndMultiply 메서드 호출/복귀 바이트코드
1. 로컬 변수 배열의 관점
- 초기 상태
- 로컬 변수 배열: [args, 0, 0, 0]
- 오퍼랜드 스택: []
iconst_5<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iconst_i>
- 상수 5를 오퍼랜드 스택에 푸시
- 변경 후
- 로컬 변수 배열: [args, 0, 0, 0]
- 오퍼랜드 스택: [5]
istore_1<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.istore>
- 오퍼랜드 스택의 값을 로컬 변수 배열의 인덱스 1에 저장
- 변경 후
- 로컬 변수 배열: [args, 5, 0, 0]
- 오퍼랜드 스택: []
iconst_10(bipush)<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.bipush>
- 상수 10을 오퍼랜드 스택에 푸시
- 변경 후
- 로컬 변수 배열: [args, 5, 0, 0]
- 오퍼랜드 스택: [10]
istore_2
- 오퍼랜드 스택의 값을 로컬 변수 배열의 인덱스 2에 저장
- 변경 후
- 로컬 변수 배열: [args, 5, 10, 0]
- 오퍼랜드 스택: []
2. 오퍼랜드 스택의 관점
iload_1<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.istore>
- 로컬 변수 배열의 인덱스 1 값을 오퍼랜드 스택에 푸시
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 0]
- 오퍼랜드 스택: [5]
iload_2
- 로컬 변수 배열의 인덱스 2 값을 오퍼랜드 스택에 푸시
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 0]
- 오퍼랜드 스택: [5, 10]
3. 메서드 호출과 복귀 주소
invokestatic #2
- addAndMultiply 메서드를 호출
- 로컬 변수 배열: [x, y, sum, product]
- 오퍼랜드 스택:
- iload_0: 5 (x)
- iload_1: 10 (y)
- iadd: 15 (sum)
- istore_2: sum 저장
- iload_0: 5 (x)
- iload_1: 10 (y)
- imul: 50 (product)
- istore_3: product 저장
- iload_2: 15 (sum)
- iload_3: 50 (product)
- iadd: 65 (결과)
- ireturn: 65 반환
2. 오퍼랜드 스택의 관점
istore_3
- 오퍼랜드 스택의 값을 로컬 변수 배열의 인덱스 3에 저장
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 55]
- 오퍼랜드 스택: []
4. 상수 풀 참조
getstatic #13<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.getstatic>
- System.out을 13번 인덱스 상수 풀에서 정적 필드를 참조하여 오퍼랜드 스택에 푸시
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 55]
- 오퍼랜드 스택: [System.out]
2. 오퍼랜드 스택의 관점
iload_3
- 로컬 변수 배열의 인덱스 3 값을 오퍼랜드 스택에 푸시
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 55]
- 오퍼랜드 스택: [System.out, 55]
3. 메서드 호출과 복귀 주소
invokevirtual #19<https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokevirtual>
- 작업: println 메서드를 호출하여 값을 출력합니다.
- 변경 후:
- 로컬 변수 배열: [args, 5, 10, 55]
- 오퍼랜드 스택: []
마무리
이외에도 자바의 JVM구조에 대해 알고 싶으면 아래 포스팅을 참고해보자
BytecodeExample 클래스를 컴파일하여 javap 명령어를 통해 분석함으로써 로컬 변수 배열, 오퍼랜드 스택, 메서드 호출과 복귀, 그리고 상수 풀의 역할을 구체적으로 알아보았는데 JVM 스택의 구성요소를 정리하고 마무리 하겠다.
- 로컬 변수 배열
- 각 메서드 호출 시 생성되는 로컬 변수 배열에 변수와 매개변수가 저장
- 예제에서 변수 a와 b가 각각 인덱스 1과 2에 저장된것을 확인할수 있다.(결과도 저장됌)
- 오퍼랜드 스택
- 명령어 실행 시 필요한 피연산자들이 오퍼랜드 스택에 푸시되고, 연산 후 다시 오퍼랜드 스택에 푸시
- 예제에서 iconst_5와 bipush 10 명령어로 각각 상수 5와 10을 오퍼랜드 스택에 푸시하고(계산을 위해), istore 명령어는 이를 로컬 변수 배열에 저장하였다.
- 메서드 호출과 복귀
- invokestatic 명령어는 정적 메서드를 호출하고 호출된 메서드가 실행되는 동안 새로운 프레임이 생성되었다.
- invokevirtual 명령어는 인스턴스 메서드를 호출하고 해당 객체의 참조와 인수를 오퍼랜드 스택에서 꺼내어 메서드를 호출했다.
- 상수 풀 참조
- getstatic 명령어는 상수 풀에서 정적 필드를 참조하여 오퍼랜드 스택에 푸시한다.
- 예제에서 System.out 필드는 상수 풀에서 참조되어 오퍼랜드 스택에 푸시되고, println 메서드를 호출하여 결과를 출력한다.
'Java' 카테고리의 다른 글
자바 가비지 컬렉터(GC)의 발전; 시리얼 컬렉터부터 ZGC까지 (0) | 2024.07.20 |
---|---|
자바가상머신의 메모리 구조: JVM Runtime Data Area (0) | 2024.07.04 |
JVM에서 자바 메서드와 네이티브 메서드 실행의 차이점 (0) | 2024.06.24 |
Optional: 안정적인 Null 처리 그리고 orElse(), orElseGet(), orElseThrow() 에 대한 이해 (0) | 2023.10.24 |
객체지향에서의 클래스 간의 관계[IS, HAS, USES, REALIZE,DEPEND-ON] (0) | 2023.04.28 |