잘 정리된 블로그가 있어 정리해봅니다 ㅎㅎ
자바 가상 머신JVM
은 자바 코드와 어플리케이션을 구동하는, 런타임 환경을 제공하는 엔진이에요. 자바 소스를 컴파일한 결과물인 바이트byte
코드를 기계어로 변환시켜줘요.
JVM은 JRE
의 일부에요. 다른 프로그래밍 언어에선 컴파일러가 일부 시스템에 적합한 기계어를 제공하는 반면, 자바 컴파일러는 JVM이라는 가상 머신이 기계어로 변환하기 위한 코드(아까 말한 바이트 코드에요)를 만들어줘요.
자바 컴파일러의 결과인 바이트 코드는 시스템 중립적이고 JVM이 시스템에 따라 적절한 기계어로 변환해주기 떄문에 모든 플랫폼에 적용할 수 있는 크로스-플랫폼Cross-platform
환경을 제공해요.
JVM의 구조는 클래스로더Classloader
, 메모리 영역Memory Area
그리고 실행 엔진Execution Engine
등으로 나눌 수 있어요.
Classloader
는 자바 컴파일러를 통해 생성된 바이트 코드, .class
확장명의 파일을 JVM에 적재Loading
해요. 초기화Intialization
, 로딩Loading
, 바이트 코드를 서로 연결하는 링크Linking
이라는 기능들을 수행해요.
적재가 완료되면 자바로 만들어진 바이트 코드들을 JVM Memory
에 저장되요.
JVM은 프로그램이 실행 중에 클래스가 사용될 때 해당 클래스 파일을 읽고 분석한 클래스의 인스턴스 변수, 메서드 코드 등을 Method Area에 저장해요. 클래스 변수도 같이 저장되죠.
물론 모든 코드가 저장되는건 아니에요. new
키워드를 통해 객체가 동적으로 생성되기 전에는 텍스트일 뿐이에요.
객체를 생성하고 메서드를 실행하면 해당 클래스 코드에 대한 정보를 Method Area에 저장하게 되요. 저장되는 목록들을 정리하면 아래와 같아요.
보통 클래스와 인스턴스를 통칭하는 걸 Type
이라고 해요.
Type
의 전체 이름(패키지, 클래스 명)Type
의 직계 하위 클래스 전체 이름Type
의 클래스/인터페이스 여부modifier(public/abstract/final)- 연관된 인터페이스 이름 목록
Type
의 모든 상수 정보를 가지고 있어요.
- Type, Field, Method의 모든 Symbolic Reference 정보를 포함
- Constant Pool의 시작점
Entry
는 배열과 같이 인덱스 번호를 통해 접근- 객체의 접근 등 모든 참조를 위한 핵심 요소
Field
는 인스턴스 변수를 가리켜요.
- Field Type
- Field modifier(public/private/protected/static/final/volatile/transient)
생성자를 포함한 모든 메서드를 저장해요.
- Method 이름
- Method 반환 타입
- Method 파라미터 수와
Type
- Method modifier
- Method 구현 부분이 있다면(abstract/native X),
- Method의 바이트 코드
- Method의 Stack Frame의 Operand Stack 및 지역 변수 섹션
section
의 크기- 예외 테이블
여기서 클래스 변수는 static 키워드로 선언된 변수를 의미해요.
클래스 변수의 특징을 정리하자면,
- 모든 인스턴스에 공유되며 인스턴스 없이도 접근이 가능해요.
- 이 변수는 인스턴스의 것이 아니라 클래스에 속해요.
- 클래스를 사용하기 이전에 이 변수들은 미리 메모리를 할당 받아 있는 상태가 되요.
- final 클래스 변수는 상수로 치환되어
Runtime Constant Pool
에 값을 복사해요.
사용자가 관리하는 인스턴스가 생성되는 공간이에요. 객체를 동적으로 생성하면 바로 이 영역의 메모리에 할당되어 사용되요. 프로그램은 시작될 때 미리 이 영역을 많이 할당해 놓으며 인스턴스와 인스턴스 변수를 저장해요.
참조 변수의 경우 Heap
에 인스턴스가 저장되는게 아니라 포인터가 저장되요.
참고로 Heap
영역은 JVM이 메모리를 관리하는데 쓰는 가비지 컬렉터Garbage Collector
의 대상이 되요.
지역 변수와 일부 결과 값을 저장해요. 자바에서 스레드는 생성과 동시에 하나의 JVM Stack을 할당 받아요. 메서드가 호출될 때마다 하나의 프레임이 생성되고, 메서드 내 프로세스가 종료되면 삭제되요.
💡프레임
Frame
이란?
메서드가 호출될 때마다 만들어지고 메서드의 상태 정보를 저장해요. 총 3가지 구성되어 있어요.
1. 지역 변수
2. Operand Stack 메서드 계산을 위한 작업 공간
3. Constant Pool Reference
현재 실행 중인 JVM의 명령을 저장해요. 운영체제에서의 프로그램 계수기Program Counter, PC
라 보시면 되요.
각 스레드는 하나의 PC Register
를 가져요.
하드웨어, 소프트웨어 또는 완전한 시스템들을 테스트하는 소프트웨어의 일종이에요.
프로그래밍 프레임워크의 일종으로, JVM에서 동작 중인 자바 코드에서 라이브러리와 네이티브 어플리케이션을 호출할 수 있게 해줘요.
:💡네이티브
Native
의 의미
에뮬레이션이나 호환 모드의 사용 등 외적인 지원 없이, 특정 하드웨어나 OS에서 그대로 실행되는 것을 의미.
Execution Engine
을 구동하는데 필요한 C/C++ 네이티브 라이브러리 집합이에요.
자바는 JVM의 존재 덕분에 크로스 플랫폼Cross-platform
언어라고 했어요. 왜 이렇게 되는지를 자세하게 알아보죠.
그림을 예로 들면, 컴파일러가 a1
, a2
, a3
자바 파일을 컴파일하면 .class
확장명의 바이트 코드들을 각 파일마다 하나씩 생성해요. 이때 까진 모든 환경(OS, CPU)에서 동일한 결과의 바이트 코드들이 생성되요.
컴파일이 완료되면 JVM이 램RAM
에 적재되면서 실행되요. 그리고 JVM의 Class Loader
를 통해 바이트 코드들을 RAM에 적재시켜요.
그 다음에 Execution Engine
이 바이트 코드들을 네이티브 머신 코드, 즉 해당 플랫폼의 코드(기계어)로 변환을 해요. JVM의 존재 덕분에 자바는 모든 플랫폼에서 동작할 수 있게 되는거에요.
그리고 바이트 코드 컴파일은 즉시Just-in-time,JIT
컴파일 방식을 취하고 있어요.
💡Just-in-time
JIT
컴파일
- 프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일하는 방식을 말해요.
- 매번 같은 코드를 즉석으로 해석하는 대신 처음 실행될 때 해석
Interpret
를 하면서 자주 쓰이는 코드를 캐싱Caching
해서 속도를 개선시켜요.
프로그래밍 언어를 접하면 각 언어의 수준Level
을 정의한 것을 들어보셨을거에요.
고
High
수준 - C++, Java
중간Middle
수준 - C
저Low
수준 - Assembly
마지막 최저Lowest
수준 - 기계어
컴파일러는 고 수준의 언어를 저수준의 언어로 번역해주는 프로그램이에요. 그리고 인터프리터Interpreter
는 특정 수준의 언어를 동일한 수준의 다른 언어로 변환을 해주는 프로그램이에요.
자바 컴파일러는 코드를 즉시 기계어로 번역해주는게 아니라 저수준의 파일인 바이트 코드로 번역을 해주죠. 그리고 JVM의 JIT 코드 생성기는 이 바이트 코드를 각 플랫폼에 맞는 기계어로 변환해줘요.
바로 이러한 컴파일 및 인터프리팅 특성을 동시에 가졌기 때문에 자바는 컴파일 언어이자 인터프리터 언어라고 할 수 있어요.
두 가지 이유 때문에 느려요.
1. Dynamic Linking
C/C++언어와 달리 자바는 번역한 소스들을 연결Link
하는 작업을 프로그램이 실행되는 중에실시간, Real time
수행해요.
2. Run-time Interpreter
바이트 코드 변환도 마찬가지 실시간으로 처리해요.
다른 컴파일 언어들은 Link
작업, 소스 코드들을 이미 기계어로 번역하는 작업을 이미 컴파일 과정에서 처리했기 때문에 자바보다 당연히 빠를 수 밖에 없어요.
하지만 이런 언어들은 프로그램을 실행시키는 CPU의 명령어, 기계어로 변환할 수 있어야 하고 운영체제마다 사용할 수 있는 API들을 제공해줘야 하기 때문에 새로운 CPU나 운영체제가 나올 때마다 컴파일러를 만들어줘야 해요. 그래서 이식성이 떨어진다고 볼 수 있어요.
자바는 JVM이 알아서 모든 플랫폼에 대응할 수 있게끔 만들어졌기 때문에 이식성이 높다고 하는거에요. 모든 것이 다 Trade-off
가 존재해요 ㅎㅎ.
JVM에 대한 내용은 여기까지에요. 요즘 한창 운영체제랑 컴퓨터 구조 공부한 내용을 정리해서 그런지 전보다 이러한 개념을 이해를 더 하게 되는 거같아요. 기본기의 중요성을 새삼 깨닫게 되네요.
guru99님 블로그
holaxprogramming님 블로그
johngrib님 블로그 - JVM Stack과 Frame