자바 가상 머신
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
자바 가상 머신(JVM)은 자바 런타임 환경의 핵심 구성 요소로, 자바 바이트코드를 실행하는 추상 컴퓨터이다. 제임스 고슬링의 영향을 받아 스택 기반으로 설계되었으며, 가비지 컬렉션, 객체 지향 프로그래밍, 타입 안전성을 특징으로 한다. JVM은 명세에 의해 정의되며, 핫스팟과 JRockit과 같은 다양한 구현체가 존재한다. 주요 기능으로는 클래스 로딩, 바이트코드 검증, JIT 컴파일 등이 있으며, 다양한 JVM 언어를 지원한다. 또한, 과거 웹 브라우저에서 자바 애플릿 실행에 사용되었으나, 현재는 웹 브라우저 플러그인 지원 중단으로 사용 빈도가 감소했다. JVM은 자바 SE, EE, ME, 카드 등 다양한 플랫폼에서 사용되며, 각 플랫폼에 맞는 규격과 구현체를 가진다.
더 읽어볼만한 페이지
- 자바 가상 머신 - 핫스팟 (가상 머신)
핫스팟은 오라클의 자바 런타임 환경에 포함된 가상 머신으로, 반복되는 코드 영역을 최적화하는 JIT 컴파일러이며, 1999년 출시 후 GNU GPL 라이선스로 사용 가능하다. - 자바 가상 머신 - 안드로이드 런타임
안드로이드 런타임(ART)은 안드로이드 운영 체제에서 애플리케이션 실행을 위한 런타임 환경이며, AOT 컴파일을 통해 실행 효율성을 높이고 전력 소비를 줄이는 장점을 가진다. - 자바 사양 요청 - 자바 플랫폼, 마이크로 에디션
자바 ME는 임베디드 및 모바일 장치에서 자바 앱을 실행하는 플랫폼으로, 피처폰에서 주로 사용되었으며 다양한 프로파일과 에뮬레이터, 개발 도구를 제공하고 JSR을 통해 기능이 확장된다. - 자바 사양 요청 - 자바 커뮤니티 프로세스
자바 커뮤니티 프로세스는 자바 기술 명세의 개발 및 관리를 담당하는 조직으로, 자바 기술 표준화와 발전에 기여해왔으나 운영 방식에 대한 비판과 여러 논란에 직면해 있다. - 자바 플랫폼 소프트웨어 - 자바 데스크톱 시스템
- 자바 플랫폼 소프트웨어 - 핫자바
핫자바는 자바 프로그래밍 언어 기반으로 개발된 웹 브라우저이며, 1995년 TED 컨퍼런스에서 처음 시연되고 SunWorld 컨퍼런스에서 공식 발표되었다.
자바 가상 머신 | |
---|---|
자바 가상 머신 정보 | |
이름 | 자바 가상 머신 |
영어 이름 | Java virtual machine |
다른 이름 | Java VM |
설계자 | 썬 마이크로시스템즈 |
비트 | 32비트 |
출시 년도 | 1994년 |
최신 버전 | 20.0.1 |
유형 | 스택 및 레지스터-레지스터 |
인코딩 | 가변 |
분기 | 비교 및 분기 |
엔디언 | Big |
개방 여부 | 예 |
범용 레지스터 | 메소드당 오퍼랜드 스택 (최대 65535개 오퍼렌드) + 메소드당 지역 변수 (최대 65535개) |
2. 특성
제임스 고슬링(James Gosling영어)에 따르면 UCSD 파스칼의 p-코드(p-code영어)와 스몰토크 VM에 영향을 받아 설계되었다.[29] JVM은 이 두 가상 머신처럼 스택 기반으로, 대부분의 명령어가 스택 선두에서 피연산자를 택하고 결과는 다시 스택에 넣는다. 스택 상의 피연산자 타입을 구분하고 명령어에 스택 상의 피연산자 타입을 기술하는 면에서는 p-코드와 유사하며,[30] 실제 구조에는 차이가 있지만 가비지 컬렉션을 사용하고 객체와 메서드 호출 개념이 있다는 점에서는 스몰토크 가상 머신과 유사하다.[31] 하지만 p-코드가 타입 안전성을 보장하지 않아 메모리 내용을 망가뜨릴 수 있다는 점, 스몰토크 가상 머신이 동적 타입을 사용하는 스몰토크 언어 자체와 마찬가지로 타입 구분을 하지 않는다는 점에서는 JVM과 다르다.
JVM의 주요 특성은 다음과 같다.[32]
- 스택 기반의 가상 머신
- 단일 상속 형태의 객체 지향 프로그래밍을 가상 머신 수준에서 구현
- 포인터를 지원하되 C와 같이 주소 값을 임의로 조작이 가능한 포인터 연산이 불가능
- 가비지 컬렉션 사용
- 모든 기본 타입의 정의를 명확히 함으로써 플랫폼 독립성 보장
- 데이터 흐름 분석(data flow analysis영어)에 기반한 자바 바이트코드 검증기(verifier영어)를 통해 스택 넘침, 명령어 피연산자의 타입 규칙 위반, 필드 접근 규칙 위반, 지역 변수의 초기화 전 사용 등 많은 문제를 실행 전에 검증하여 실행 시 안전을 보장하고 별도의 부담을 줄여줌
- 명령어에서 스택에서 가져올 피연산자의 타입을 명령어에 지정(예: 정수 덧셈은 ''iadd'', 단정밀도 실수 덧셈은 ''fadd'')
3. JVM 사양
자바 가상 머신(JVM)은 명세에 의해 정의된 추상적인(가상) 컴퓨터로, 자바 런타임 환경(JRE)의 일부이다. 가비지 컬렉션 알고리즘 및 JVM 명령어의 내부 최적화(명령어의 기계어로 변환)는 명세에 포함되지 않아 구현자에게 자율성을 부여한다.[2] 모든 자바 애플리케이션은 JVM 추상 명세의 구체적인 구현 내에서만 실행될 수 있다.
자바 플랫폼, 스탠다드 에디션(J2SE) 5.0부터 JVM 명세 변경 사항은 JSR 924로서 자바 커뮤니티 프로세스 하에 개발되었다.[3] 클래스 파일 형식(JSR 202)에 제안된 변경 사항을 지원하기 위한 명세 변경은 JSR 924의 유지 보수 릴리스로 진행되고 있다.[4] JVM 명세는 "블루 북"으로 출판되었으며,[5] 썬 마이크로시스템즈는 이 명세가 호환 가능한 클린룸 구현을 가능하게 할 것이라고 예상했다.
오라클의 JVM에는 핫스팟과 BEA 시스템즈로부터 상속받은 JRockit이 있다. 오라클은 구현 제품군이 오라클의 명세와 완전히 호환됨을 인증하기 위해 자바 상표 사용을 허용한다.
3. 1. 클래스 로더
클래스 로더는 다음 세 가지 기본 작업을 엄격한 순서대로 수행한다.[1]1. 로딩(Loading): 형식에 대한 이진 데이터를 찾아 가져온다.[1]
2. 연결(Linking): 검증, 준비 및 (선택 사항) 해결을 수행한다.[1]
- 검증(Verification): 가져온 형식의 정확성을 확인한다.[1]
- 준비(Preparation): 클래스 변수에 대한 메모리를 할당하고 메모리를 기본값으로 초기화한다.[1]
- 해결(Resolution): 형식의 기호 참조를 직접 참조로 변환한다.[1]
3. 초기화(Initialization): 클래스 변수를 적절한 시작 값으로 초기화하는 자바 코드를 호출한다.[1]
일반적으로 부트스트랩 클래스 로더, 확장 클래스 로더, 시스템/애플리케이션 클래스 로더의 세 가지 유형의 클래스 로더가 있다.[1]
JVM 바이트 코드의 구성 단위 중 하나는 클래스이다. 클래스 로더 구현은 자바 클래스 파일 형식에 부합하는 모든 것을 인식하고 로드할 수 있어야 한다.[1] 어떤 구현이든 'class' 파일 외 다른 이진 형식을 인식할 수 있지만, 'class' 파일은 반드시 인식해야 한다.[1]
모든 자바 가상 머신 구현에는 신뢰할 수 있는 클래스를 로드할 수 있는 부트스트랩 클래스 로더와 확장 클래스 로더 또는 애플리케이션 클래스 로더가 있어야 한다.[1] 자바 가상 머신 사양은 클래스 로더가 클래스를 어떻게 찾아야 하는지에 대해서는 명시하지 않는다.[1]
3. 2. 가상 머신 아키텍처
JVM은 기본 유형(정수, 부동 소수점 등)과 참조 유형의 데이터를 처리한다.[6] 초기 JVM은 32비트 머신이었으나, 최신 JVM (OpenJDK HotSpot JVM)은 64비트를 지원하여 더 큰 주소 공간을 활용할 수 있다. 64비트 환경에서 Java를 실행하면 더 큰 Java 힙 크기와 증가된 최대 Java 스레드 수를 허용하지만, 32비트 JVM에 비해 성능 저하가 발생할 수 있다.객체와 배열은 가비지 컬렉션 힙에 저장되며, 코드, 상수 등은 메서드 영역에 저장된다. 메서드 영역은 논리적으로 힙의 일부이지만, 구현에 따라 힙과 별도로 처리될 수 있다. 각 JVM 스레드는 자체 호출 스택을 가지며, 메서드 호출 시 프레임이 생성되고 종료 시 파괴된다.
각 프레임은 피연산자 스택과 지역 변수 배열을 포함한다. 피연산자 스택은 계산을 위한 피연산자와 호출된 메서드의 반환 값을 수신하는 데 사용되는 반면, 지역 변수는 레지스터와 같은 목적으로 사용되며 메서드 인수를 전달하는 데에도 사용된다.
3. 3. 바이트코드 명령어
JVM은 로드 및 스토어, 산술 연산, 자료형 변환, 객체 생성 및 조작, 오퍼랜드 스택 관리 (푸시/팝), 제어 전달 (분기), 메서드 호출 및 반환, 예외 발생, 모니터 기반 동시성 등 다양한 작업을 위한 명령어를 제공한다.[32] 이러한 명령어 집합은 특정 명령어 집합 아키텍처의 네이티브 자료형이 아닌 추상화된 DATA_TYPE 집합에서 작동한다.[32]JVM 명령어는 스택 기반으로 동작하며, 대부분의 명령어는 스택에서 피연산자를 가져와 연산을 수행하고 결과를 다시 스택에 넣는 방식으로 작동한다. 명령어는 수행할 연산의 종류와 피연산자의 타입을 나타내는 코드로 구성된다. 예를 들어, `iadd`는 정수 덧셈을, `fadd`는 단정밀도 부동 소수점 덧셈을 나타낸다.[32]
다음은 바이트코드 명령어의 몇 가지 예시이다.
- 값을 스택에 푸시:
- `bipush`, `sipush`: 바이트 값, 쇼트 값을 스택에 푸시한다.
- `ldc`, `ldc_w`, `ldc2_w`: 상수 풀에서 정수, float, java.lang.String, long, double 값을 스택에 푸시한다.
- `iconst_m1` ~ `iconst_5`, `lconst_0`, `lconst_1`, `fconst_0` ~ `fconst_2`, `dconst_0`, `dconst_1`: 각각 int, long, float, double 형의 특정 값(-1 ~ 5, 0 또는 1, 0 또는 1 또는 2, 0 또는 1)을 스택에 푸시한다.
- `aconst_null`: null 값을 스택에 푸시한다.
- 스택에서 값을 제거/조작:
- `pop`, `pop2`: 스택에서 1, 2워드를 제거한다.
- `dup`, `dup2`, `dup_x1`, `dup2_x1`, `dup_x2`, `dup2_x2`: 스택의 값을 복사한다.
- `swap`: 스택 최상위 두 값을 교환한다.
- 지역 변수에서 값을 가져오거나 저장:
- `iload`, `lload`, `fload`, `dload`, `aload`: 지역 변수에서 `int`, `long`, `float`, `double`, 참조 값을 가져와 스택에 푸시한다.
- `iload_0` ~ `iload_3`, `lload_0` ~ `lload_3`, `fload_0` ~ `fload_3`, `dload_0` ~ `dload_3`, `aload_0` ~ `aload_3`: 0, 1, 2, 3번째 지역 변수 값을 스택에 푸시한다.
- `istore`, `lstore`, `fstore`, `dstore`, `astore`: `int`, `long`, `float`, `double`, 참조 값을 지역 변수에 저장한다.
- `istore_0` ~ `istore_3`, `lstore_0` ~ `lstore_3`, `fstore_0` ~ `fstore_3`, `dstore_0` ~ `dstore_3`, `astore_0` ~ `astore_3`: `int`, `long`, `float`, `double`, 참조 값을 0, 1, 2, 3번째 지역 변수에 저장한다.
- 조건부 분기:
- `ifeq`, `ifnull`, `iflt`, `ifle`, `ifne`, `ifnonnull`, `ifgt`, `ifge`: 스택 값이 0, `null`, 0 미만, 0 이하, 0 이외, `null` 이외, 0 초과, 0 이상인 경우 지정된 주소로 제어를 이동한다.
- `if_icmpeq`, `if_icmpne`, `if_icmplt`, `if_icmpgt`, `if_icmple`, `if_icmpge`: 두 `int` 값이 같음, 같지 않음, <, >, ≤, ≥인 경우 지정된 주소로 제어를 이동한다.
- `if_acmpeq`, `if_acmpne`: 두 참조 값이 같음, 같지 않음인 경우 지정된 주소로 제어를 이동한다.
- 형 변환:
- `i2l`, `i2f`, `i2d`, `i2b`, `i2c`, `i2s`: `int` 값을 `long`, `float`, `double`, `byte`, `char`, `short` 값으로 변환한다.
- `l2i`, `l2f`, `l2d`: `long` 값을 `int`, `float`, `double` 값으로 변환한다.
- `f2i`, `f2l`, `f2d`: `float` 값을 `int`, `long`, `double` 값으로 변환한다.
- `d2i`, `d2l`, `d2f`: `double` 값을 `int`, `long`, `float` 값으로 변환한다.
- 비교 연산:
- `dcmpg`, `dcmpl`: 두 `double` 값을 비교하여, 큰 경우 `1`, 같은 경우 `0`, 작은 경우 `-1`을 스택에 남긴다.
- `fcmpg`, `fcmpl`: 두 `float` 값을 비교한다.
- `lcmp`: 두 `long` 값을 비교한다.
- 산술 연산:
- `iadd`, `ladd`, `fadd`, `dadd`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 첫 번째 값에 두 번째 값을 더하고, 스택에 쌓는다.
- `isub`, `lsub`, `fsub`, `dsub`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 첫 번째 값에서 두 번째 값을 빼고, 스택에 쌓는다.
- `imul`, `lmul`, `fmul`, `dmul`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 첫 번째 값에 두 번째 값을 곱하고, 스택에 쌓는다.
- `idiv`, `ldiv`, `fdiv`, `ddiv`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 첫 번째 값을 두 번째 값으로 나눈 몫을 스택에 쌓는다.
- `irem`, `lrem`, `frem`, `drem`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 첫 번째 값을 두 번째 값으로 나눈 나머지를 스택에 쌓는다.
- `ineg`, `lneg`, `fneg`, `dneg`: `int`, `long`, `float`, `double` 값에서 스택에서 가져온 값의 부호를 반전시킨다.
- 시프트 및 논리 연산:
- `ishl`, `lshl`: `int`, `long` 값을 지정한 비트만큼 왼쪽으로 시프트한다.
- `ishr`, `lshr`: `int`, `long` 값을 지정한 비트만큼 오른쪽으로 산술 시프트한다.
- `iushr`, `lushr`: `int`, `long` 값을 지정한 비트만큼 오른쪽으로 논리 시프트한다.
- `iand`, `land`: `int`, `long` 값 2개의 피연산자의 `AND` (비트 단위 논리곱)을 구한다.
- `ior`, `lor`: `int`, `long` 값 2개의 피연산자의 `OR` (비트 단위 논리합)을 구한다.
- `ixor`, `lxor`: `int`, `long` 값 2개의 피연산자의 `XOR` (비트 단위 배타적 논리합)을 구한다.
- 기타:
- `checkcast`: 참조 값이 가리키는 인스턴스 형식을 확인한다.
- `instanceof`: 스택에서 참조 값을 제거하고, 그것이 지정된 형식과 같으면 `1`을, 다르면 `0`을 스택에 푸시한다.
- `iinc`: `int` 값을, 부호 있는 1바이트(값의 범위는 -128~127)로 직접 기술한 상수만큼 증감시킨다.
3. 3. 1. 명령어 목록 (니모닉)
nopaconst_null
iconst_m1
iconst_0
iconst_1
iconst_2
iconst_3
iconst_4
iconst_5
lconst_0
lconst_1
fconst_0
fconst_1
fconst_2
dconst_0
dconst_1
bipush
sipush
ldc
ldc_w
ldc2_w
iload
lload
fload
dload
aload
iload_0
iload_1
iload_2
iload_3
lload_0
lload_1
lload_2
lload_3
fload_0
fload_1
fload_2
fload_3
dload_0
dload_1
dload_2
dload_3
aload_0
aload_1
aload_2
aload_3
iaload
laload
faload
daload
aaload
baload
caload
saload
istore
lstore
fstore
dstore
astore
istore_0
istore_1
istore_2
istore_3
lstore_0
lstore_1
lstore_2
lstore_3
fstore_0
fstore_1
fstore_2
fstore_3
dstore_0
dstore_1
dstore_2
dstore_3
astore_0
astore_1
astore_2
astore_3
iastore
lastore
fastore
dastore
aastore
bastore
castore
sastore
pop
pop2
dup
dup_x1
dup_x2
dup2
dup2_x1
dup2_x2
swap
iadd
ladd
fadd
dadd
isub
lsub
fsub
dsub
imul
lmul
fmul
dmul
idiv
ldiv
fdiv
ddiv
irem
lrem
frem
drem
ineg
lneg
fneg
dneg
ishl
lshl
ishr
lshr
iushr
lushr
iand
land
ior
lor
ixor
lxor
iinc
i2l
i2f
i2d
l2i
l2f
l2d
f2i
f2l
f2d
d2i
d2l
d2f
i2b
i2c
i2s
lcmp
fcmpl
fcmpg
dcmpl
dcmpg
ifeq
ifne
iflt
ifge
ifgt
ifle
if_icmpeq
if_icmpne
if_icmplt
if_icmpge
if_icmpgt
if_icmple
if_acmpeq
if_acmpne
goto
jsr
ret
tableswitch
lookupswitch
ireturn
lreturn
freturn
dreturn
areturn
return
getstatic
putstatic
getfield
putfield
invokevirtual
invokespecial
invokestatic
invokeinterface
invokedynamic
new
newarray
anewarray
arraylength
athrow
checkcast
instanceof
monitorenter
monitorexit
wide
multianewarray
ifnull
ifnonnull
goto_w
jsr_w
breakpoint
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
정의되지 않음
impdep1
impdep2