JVM?
👉 JVM 특성
자바 애플리케이션을 실행하기 위한 환경을 제공하는 소프트웨어.
자바 가상 머신(JVM)의 주요 목적은 여러 운영체제에 동일하게 실행되게끔 하는 것. (Write Once, Run Anywhere)
자바 이외의 언어로 작성된 코드도 실행할 수 있는 기능을 제공. (스칼라, 코틀린, ...)
플랫폼 독립성
- 자바 소스 코드는 운영체제에 독립적인 바이트 코드로 컴파일됨.
이 바이트 코드는 다양한 운영체제에서 JVM에 의해 실행됨.
실행 엔진 (인터프리터, JIT)
- JVM은 바이트 코드를 실제 기계어로 변환하여 실행함.
- 이 과정에서는 인터프리터 실행과 JIT(Just-In-Time) 컴파일이 포함됨.
JIT 컴파일러는 바이트 코드를 더 빠르게 기계어로 변환하여 프로그램 성능을 향상시킴.
메모리 관리
- JVM은 프로그램의 메모리 할당과 관리를 하는데 가비지 컬렉션(Garbage Collection)이 포함되어 있음.
보안
- JVM은 코드 실행에 대한 보안을 제공. 코드가 시스템에 해를 끼치지 않도록 메모리 관리 및 엑세스 권한을 관리.
클래스 로더 시스템
- 클래스 로더를 통해 자바 클래스 파일을 로드하고 링크하여 런타임 데이터 영역에 배치.
- 이 과정에서 클래스의 검증, 준비, 해석이 이루어짐.
- 클래스를 안전하고 효율적으로 로드하고 링크하고 초기화 시킴.
- 자바의 강력한 보안, 견고함, 플랫폼 독립성을 지원하는 데 기여함.
런타임 데이터 영역
- JVM은 메모리에 여러 런타임 데이터 영역을 유지,관리.
- 메서드 영역, 힙, 스택, 프로그램 카운터 레지스터, 네이티브 메서드 스택이 포함.
👉 JVM 실행 과정?
1. 소스코드 작성
프로그래머가 '.java' 확장자를 가진 파일에 자바 소스코드를 작성.
2. javac 컴파일
'.java' 파일의 소스코드를 바이트 코드로 변환.
바이트 코드는 '.class' 파일에 저장됨.
이 과정에서 소스코드의 문법 오류를 검사하고 필요한 경우 오류 메시지를 출력.
javac에 의해 생성된 바이트 코드는 모든 JVM에서 실행 가능.
3. JVM 실행
3-1) 프로세스 생성
자바 애플리케이션을 실행하면 운영체제는 JVM을 위한 프로세스를 생성 (메모리 로드)
3-2) 메모리 할당
JVM 프로세스는 자바 애플리케이션 실행에 필요한 메모리를 할당받음.
여기에는 힙, 스택, 메서드 영역 등이 포함.
3-3) 자바 바이트 코드 실행
자바 애플리케이션의 바이트 코드를 실행.
이때 JVM 내부의 JIT 컴파일러가 바이트 코드를 기계어로 변환하여 운영체제에서 실행.
👉 JVM 메모리구조?
Class Loader (클래스 로더)
자바 클래스 파일을 로드하고 링크하여 런타임 데이터 영역에 동적으로 배치.
런타임 시 동적으로 클래스를 로드.
클래스를 안전하고 효율적으로 로드하고 링크하고 초기화 시킴.
자바의 강력한 보안, 견고함, 플랫폼 독립성을 지원하는 데 기여함.
Execution Engine (실행 엔진)
클래스 로더를 통해 JVM내의 Runtime Data Area에 배치된 바이트 코드들을 명령어 단위로 읽어서 실행.
인터프리터 방식과 JIT 컴파일러 방식으로 실행.
Garbage Collector (가비지 컬렉터)
힙 메모리 영역에 생성된 객체들 중 참조되지 않은 객체들을 탐색 후 제거하는 역할.
언제 수행되는지 수행 시간은 정확히 알 수 없음.
Runtime Data Area
1) Method area
- 용도
클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보와 같은 각종 필드 정보들과 메서드 정보, 데이터 타입 정보, Constant Pool, static 변수, final class 등의 바이트 코드를 보관.
모든 쓰레드가 공유하는 메모리 영역.
- 특징
JVM 시작 시 생성. 각 클래스 구조에 대한 메타데이터를 보유.
- 관리
GC의 대상이 될 수 있지만, Heap에 비해 빈도수가 낮음.
2) Heap area
- 용도
객체와 배열같은 new 키워드로 생성된 런타임 데이터를 저장.
이는 애플리케이션 전체에서 공유되는 영역.
- 특징
GC의 주요 대상영역.
- 관리
객체의 생명주기에 따라 동적으로 할당 및 해제
3) Stack area
- 용도
각 스레드에 대한 실행 스택을 제공.
메서드 호출 시 마다 프레임이 추가되며 메서드가 종료되면 프레임이 제거됨.
- 특징
각 스레드는 자신만의 스택을 가지며 매개변수, 지역변수, 리턴 값, 메서드 호출정보 등을 저장.
- 관리
스레드마다 개별적으로 생성되며 스레드가 종료되면 해당 스택 사라짐.
4) Program Counter(PC) Register
- 용도
현재 스레드가 실행중인 JVM의 명령어 주소를 가짐.
- 특징
각 스레드마다 하나씩 존재하며 현재 실행중인 명령어의 위치를 추적.
5) Native Method Stack
- 용도
자바 외의 언어로 작성된 네이티브 메서드를 실행하기 위한 스택.
- 특징
JNI(Java Natice Interface)를 통해 네이티브 메서드 호출 시 사용.
JIT(Just-In-Time) 컴파일러?
👉 정의와 목적
자바 런타임 환경의 핵심 구성 요소 중 하나.
JVM이 바이트 코드를 로드하고 실행하는 과정에서 JIT 컴파일러가 사용.
- JVM 내부에 존재하며 바이트 코드 실행 시점에 기계어로 변환.
- 프로그램이 실행되는 동안에 실시간으로 바이트 코드를 기계어로 변환하는데 이를 '동적 번역', '런타임 컴파일' 이라고 함.
- 프로그램의 실행 속도를 향상시킴.
- 초기에는 바이트 코드가 인터프리터 방식으로 실행되지만, 자주 실행되는 코드는 JIT 컴파일러에 의해 최적화된 기계어로 변환.
인터프리터 방식으로는 속도가 느리다는 점이 있어 이를 보완하기 위해 JIT 컴파일러를 도입.
- 변환된 기계어 코드는 캐시에 저장되어 동일한 코드가 다시 실행될 때 빠르게 로드.
👉 JIT 컴파일의 과정
1. 바이트 코드 로드
JVM은 자바 애플리케이션의 바이트 코드를 로드
2. 코드 분석 및 최적화
JIT 컴파일러는 실행 중인 프로그램의 바이트 코드를 분석하고 특히 자주 사용되는 부분(핫스팟)을 식별.
3. 기계어로 컴파일
핫스팟으로 식별된 코드는 기계어로 컴파일됨.
기계어로 변환된 코드는 캐시에 저장되어 다음에 같은 코드가 실행될 때 빠르게 로드.
4. 실행
컴파일된 기계어 코드는 CPU에 의해 직접 실행됨.
👉 JIT 컴파일러의 장점
- 성능 향상 : 실행시점에 프로그램의 성능을 최적화. (특히 반복적으로 사용되는 코드에 큰 이점을 제공)
- 플랫폼 최적화 : 실행되는 시스템의 특정 아키텍처에 맞춰 코드를 최적화.
즉 다양한 환경에서 자바 애플리케이션이 최적의 성능으로 실행됨.
- 동적 최적화 : 프로그램의 실행 패턴을 분석하여 실시간으로 최적화 수행 가능.
👉 JIT 컴파일러의 단점
- 초기 성능 지연 : JIT 컴파일러는 실행 시간에 일어나므로 프로그램이 처음 실행될 때 일시적인 성능 지연 발생 가능성 있음. (컴파일 지연)
- 메모리 사용 증가 : JIT 컴파일러는 컴파일된 코드를 저장하기 위해 추가 메모리를 사용.
많은 메모리를 사용하는 애플리케이션에서는 이로 인한 메모리 부담이 커질 수 있음.
- 최적화의 복잡성 : 다양한 최적화 기법을 사용하기에 기법의 복잡성으로 인해 컴파일러 자체의 구현이 복잡해질 수 있음.