CS 스터디 9주차
JVM(Java Virtual Machine)
JVM(Java Virtual Machine)
프로그램을 실행하기 위해 자바언어로 작성된 가상머신으로,
특정 운영 체제나 하드웨어에 종속되지 않고 자바 프로그램을 여러 플랫폼에서 실행할 수 있도록 하기 위해 설계되어있다.
한 번 작성한 코드를 어디서나 실행할 수 있게 해주는데에 중요한 역할을 한다는 것으로도 볼 수 있으며
정리해보면 자바 프로그램의 이식성과 효율성을 보장하며, 다양한 운영 체제 및 하드웨어 환경에서 일관된 실행이 가능하다.
JVM은 런타임 환경에서 실행이 되며 메모리 관리, 스레드 관리, 바이트 코드 실행, 에외처리 등
프로그램을 실행하기 위해 필요한 환경을 제공하는데, 동작하는 위치는 1주차 스터디인 자바 컴파일을 다루며 일부 작성해놨다.
JVM의 특징
플랫폼 독립성
JVM은 플랫폼에 독립적인 바이트 코드를 실행하므로, 한 번 작성한 자바 프로그램은 어떤 운영 체제나 하드웨어에서도 실행될 수 있다.
자동 메모리 관리
가비지 컬렉터를 통해 더 이상 사용되지 않는 객체를 자동으로 제거하여 메모리 누수를 방지한다.
성능 향상
JIT(Just-In-Time) 컴파일러를 통해 인터프리터에 비해 더 높은 실행 속도를 제공하며
JIT 컴파일러는 실행 시간에 바이트 코드를 기계어로 변환하여 최적화된 코드를 생성한다.
보안 강화
JVM은 자바 언어의 보안 기능을 지원하며, 애플리케이션의 안전한 실행을 보장하기 위해 다양한 보안 메커니즘을 제공한다.
JVM의 구성요소
JVM은 기본적인 구성요소와 JVM의 내부 실행 메커니즘에 관련된 구성요소로 나누어볼 수 있다.
두 가지는 개념적으로는 중복이 될 수 있지만 어떤 관점에서 다루냐에 따라 명확히 구분이 되기도 한다.
먼저 기본적인 구성요소를 살펴보면
1. 클래스 로더(Class Loader)
자바 클래스 파일을 JVM 내부로 로드하고 링크하는 역할을 담당하며,
클래스 로더는 세 가지 계층(부트스트랩 클래스 로더, 확장 클래스 로더, 애플리케이션 클래스 로더)으로 구성되어 있다.
부트스트랩 클래스 로더(Bootstrap Class Loader)
부트스트랩 클래스 로더는 JVM의 일부로서 가장 먼저 로드되는 클래스 로더로
주로 자바의 핵심 라이브러리(Java API)를 로드한다.
이 라이브러리는 자바의 기본 데이터 타입, 배열, 컬렉션 등과 같은 핵심 클래스를 포함하며
부트스트랩 클래스 로더는 네이티브 코드로 구현되어 있으며, 주로 C나 C++로 작성되어 있다.
또한, 부트스트랩 클래스 로더는 JVM의 일부로서 자바 코드로 직접 액세스할 수 없다는 특징도 존재한다.
확장 클래스 로더(Extension Class Loader)
확장 클래스 로더는 부트스트랩 클래스 로더의 자식 클래스 로더이며, 주로 JVM의 확장 라이브러리(Extension API)를 로드하는데,
확장 라이브러리는 자바 환경을 확장하고 특정 기능을 제공하는 클래스와 라이브러리를 포함하고
확장 클래스 로더는 Java 환경 변수인 java.ext.dirs에 지정된 디렉토리에서 클래스를 로드하는데,
기본적으로 ${java.home}/lib/ext 디렉토리에 위치한 클래스를 로드한다.
예를 들어, 서드파티 라이브러리나 플러그인이 여기에 해당될 수 있다.
애플리케이션 클래스 로더(Application Class Loader)
애플리케이션 클래스 로더는 자바 애플리케이션의 클래스를 로드하는 역할로
주로 애플리케이션 클래스 패스(Application Classpath)에 포함된 클래스를 로드한다.
애플리케이션 클래스 패스는 사용자가 지정한 클래스와 라이브러리가 있는 디렉토리 및 JAR 파일들의 집합으로
이 클래스 로더는 보통 자바 애플리케이션을 실행할 때 자동으로 생성되며, 주로 개발자가 직접 제어하거나 구성할 필요가 없다.
이러한 클래스 로더들은 계층적으로 구성되어 있으며, 각각의 역할은 JVM의 클래스 로딩 메커니즘에서 중요한 역할을하고
클래스 로더들은 클래스를 로드하고 링크하는 과정에서 서로 협력하여 자바 애플리케이션의 실행 환경을 구성한다.
2. 런타임 데이터 영역(Runtime Data Area)
JVM은 메모리를 여러 영역으로 나누어 관리하는데
이러한 영역에는 메서드 영역, 힙, JVM 스택, 네이티브 메서드 스택, PC(Program Counter) 레지스터 등이 포함되어 있다.
메서드 영역(Method Area 또는 PermGen)
메서드 영역은 JVM이 로드한 클래스와 메서드에 대한 메타데이터를 저장하며,
클래스의 구조(메서드와 필드의 정보), 상수 풀(Constant Pool), 정적 변수(static variables) 등이 여기에 포함된다.
이 영역은 모든 스레드가 공유하며, JVM이 시작될 때 생성되고 프로그램이 종료될 때 소멸된다.
PermGen(Permanent Generation)이라고도 불리는데,
이는 이전에는 메모리 누수와 관련된 문제가 있었지만 JDK 8부터는 Metaspace로 대체되었다.
힙(Heap)
힙은 객체 인스턴스와 배열을 저장하는 영역으로, 동적으로 생성된 모든 객체는 힙에 할당되며
프로그램이 실행되는 동안 계속해서 크기가 커지거나 작아질 수 있다.
가비지 컬렉터가 메모리 관리를 담당하여 더 이상 사용되지 않는 객체를 식별하고 제거한다.
JVM 스택(JVM Stack)
JVM 스택은 각 스레드마다 생성되며, 스레드가 메서드를 호출할 때마다 호출 스택(Frame)을 구성하며,
각 호출 스택에는 메서드 호출 시 생성되는 지역 변수, 매개변수, 임시 데이터 등이 저장된다.
스택 프레임은 메서드 호출의 시작과 종료 시점에 생성되고 소멸되며, 이를 통해 메서드 호출 간 데이터의 순서와 상태를 관리한다.
네이티브 메서드 스택(Native Method Stack)
네이티브 메서드 스택은 네이티브 코드(C, C++ 등으로 작성된 코드)가 호출되는 경우에 사용되며
일반적인 자바 메서드 호출과 유사하지만, 네이티브 메서드 스택은 네이티브 코드의 호출 및 실행을 관리한다.
또한 이 영역도 JVM 스택과 유사하게 동작하지만, 네이티브 코드에서 사용되는 데이터 및 상태를 저장한다.
3. 실행 엔진(Execution Engine)
실행 엔진은 클래스 로더를 통해 로드된 바이트 코드를 실행하는 역할을 수행하며
주요 구성 요소로는 인터프리터와 JIT(Just-In-Time) 컴파일러가 있다.
이건 아래에서 따로 설명해보려고 한다.
4. 가비지 컬렉터(Garbage Collector)
가비지 컬렉터는 더 이상 사용되지 않는 객체를 식별하여 메모리에서 해제하는데,
이를 통해 메모리 누수를 방지하고 자원을 효율적으로 관리할 수 있도록 도움을 준다.
5. 네이티브 인터페이스(Native Interface)
자바 언어로 작성된 프로그램이 네이티브 코드(다른 언어로 작성된 코드)와 상호 작용할 수 있도록 지원하며,
JNI(Java Native Interface)를 통해 자바 코드와 네이티브 코드 간의 상호 작용이 가능하도록 한다.
실행 엔진의 구성요소로 있었던,내부 실행 메커니즘에 관련된 구성요소를 따로 빼서 적어본다.아무래도 중요하니까!?
1. 자바 인터프리터(Java Interpreter)
자바 바이트 코드를 한 줄씩 읽고 해석해서 해당 플랫폼에서 실행 가능한 명령어로 변환한 뒤
해석된 명령어는 해당 플랫폼 CPU에서 직접 실행되며,프로그램이 반복되는 루프 등의
코드일 경우에도 매번 바이트 코드를 해석하고 실행한다.또 자바 프로그램을 플랫폼에 독립적으로 실행이 가능하다는 특징이 있다.
장점- 구현이 간단하고 이해가 쉽다.- 컴파일 과정이 없기 때문에 초기 실행에 대해 빠른 반응
- 바이트 코드를 직접 실행하기 때문에 플랫폼에 종속적이지 않다.
단점- 바이트 코드를 한 줄 한 줄 실행하기 때문에 네이티브 코드에 비해 실행 속도가 느릴 수 있다.
- 반복적으로 실행되는 코드의 경우 매번 해석해야 하므로 성능이 저하될 수 있다.
2. JIT 컴파일러(Just-In-Time Compiler)
JIT 컴파일러도 JVM의 실행 엔진 중 하나로,
인터프리터에 비해 더 빠른 실행 속도를 제공하기 위해 바이트 코드를 기계어로 변환하는 역할을 한다.
JIT 컴파일러는 프로그램이 실행되면 인터프리터가 바이트 코드를 해석하고
만약 반복적으로 실행되는 코드가 발견되면 해당 바이트 코드를 네이티브 기계 코드로 컴파일한다.컴파일된 코드는 해당 플랫폼에서 직접 실행이 되고 이후 같은 코드가 실행될 때는 컴파일 코드가 실행된다.또한 캐시에 저장되어 나중에 동일한 코드가 실행될 때 재사용되기도 한다.
장점
- 프로그램 실행 중에 바이트 코드를 네이티브 기계 코드로 컴파일하여 실행하기 때문에 실행 속도가 향상
- 반복적으로 실행되는 코드는 컴파일된 코드로 최적화되어 성능을 향상
단점
- JIT 컴파일러가 바이트 코드를 컴파일하는 과정이 필요하기 때문에 초기 부팅 시간이 느린 편
- 컴파일된 코드를 메모리에 보관해야 하므로 메모리 사용량이 증가한다.
프로그램을 실행하는 동안 인터프리터에 의해 반복적으로 실행되는 코드를 식별하고, 해당 코드를 컴파일하여 기계어로 변환한다,
컴파일된 코드는 캐시에 저장되어 나중에 동일한 코드가 실행될 때 재사용되며, 이를 통해 실행 속도가 향상된다.
인터프리터와 달리 반복적으로 실행되는 코드를 식별하고, 해당 코드를 컴파일하여 최적화된 기계 코드로 변환합니다.
이렇게 변환된 코드는 캐시에 저장되어 재사용되며, 실행 시간을 단축시키고 성능을 향상시킵니다.
비교를 해보자면,
자바 인터프리터는 바이트 코드를 해석하여 실행하는 방식으로 동작하며, 반복적으로 실행되는 코드에 대해 매번 해석하는 반면에
JIT 컴파일러는 프로그램 실행 중에 반복적으로 실행되는 코드를 식별하여 해당 부분을 네이티브 기계 코드로 컴파일하여 실행 속도를 향상시킨다.
컴파일된 코드는 이후 실행될 때 재사용되며, 이를 통해 반복적인 해석과 실행을 피할 수 있다.
3. 런타임 시스템(Runtime System)
런타임 시스템은 JVM의 실행 환경을 관리하고 프로그램의 실행을 지원하는 일련의 시스템 및 라이브러리를 포함하고
런타임 시스템은 메모리 관리, 스레드 관리, 예외 처리, 입출력(IO), 네트워킹 등의 기능을 제공한다.
JVM의 구성 요소 중에서도 런타임 데이터 영역(Runtime Data Area)과 가비지 컬렉터(Garbage Collector)가
런타임 시스템의 일부로 간주되기도 한다.
면접 예상 질문과 답변
Q1. JVM의 역할은 무엇인가?
A1. JVM은 자바 프로그램을 실행하기 위한 가상 머신으로, 자바 프로그램이 특정 운영 체제나 하드웨어에 종속되지 않고
여러 플랫폼에서 실행될 수 있도록 합니다. 또한 이식성과 효율성을 보장하며, 프로그램의 메모리 관리, 스레드 관리,
바이트 코드 실행, 예외 처리 등을 담당합니다.
Q2. JVM의 주요 특징은?
A2. JVM은 플랫폼에 독립적인 바이트 코드를 실행하여 어떤 환경에서도 실행이 가능하고
가비지 컬렉터를 통해 사용되지 않는 객체를 제거에 메모리 누수를 방지합니다.
또한 다양한 보안 메커니즘을 제공하기 때문에 자바 언어의 보안을 보장하고 JIT 컴파일러를 통해 인터프리터보다
더 높은 실행 속도를 제공한다는 특징이 있습니다.
Q3. JVM의 구성 요소에는 무엇이 있는가?
A3. JVM의 구성요소는 클래스 로더, 런타임 데이터 영역, 실행 엔진, 가비지 컬렉터, 네이티브 인터페이스가 있으며
실행엔진 내에서는 자바 인터프리터, JIT 컴파일러, 런타임 시스템이 존재합니다.
Q4. JVM의 클래스 로더에는 어떤 종류가 있으며 각각의 역할은?
A4. 부트스트랩 클래스 로더, 확장 클래스 로더, 애플리케이션 클래스로더가 존재하며
부투스트랩 클래스 로더는 JVM의 일부로 자바의 핵심 라이브 러리를 로드하고
확장 클래스 로더는 JVM의 확장 라이브러리를 로드합니다.
또 애플리케이션 클래스로더는 자바 애플리케이션의 클래스를 로드합니다.
Q5.런타임 데이터 영역에는 어떤 종류의 영역이 있으며 각각의 역할?
A5. 런타임 데이터 영역에는 메서드와 힙, JVM스택과 네이티브 메서드 스택이 있습니다.
메서드 영역에서는 클래스와 메서드의 메타 데이터가 저장되고 힙은 객체 인스턴스와 배열 등이 저장됩니다.
JVM스택은 각 스레드마다 호출 스택을 구성하여 메서드 호출에 필요한 데이터를 저장하고
네이티브 메서드 스택은 네이티브 코드의 호출과 실행을 관리하는 역할을 합니다.
Q6. 자바 인터프리터와 JIT 컴파일러의 차이점은?
A6. 자바 인터프리터는 바이트 코드를 한 줄씩 읽고 해석하여 실행하며,
초기 실행 속도는 빠르지만 반복 실행되는 코드의 경우 성능이 저하될 수 있습니다.
반면 JIT 컴파일러는 프로그램 실행 중에 반복적으로 실행되는 코드를 식별하여 해당 부분을 네이티브 기계 코드로
컴파일하여 실행 속도를 향상시키며 초기 부팅 시간은 느리지만 실행 속도가 빠르고 메모리 사용량이 감소합니다.
Q7. JIT 컴파일러의 장점과 단점은?
A7. 장점은 프로그램 실행 중 바이트 코드를 기계어로 컴파일 하여 실행하기 때문에 실행 속도가 빠르고
반복되는 코드는 컴파일된 코드로 캐싱되어 성능이 향상되지만
JIT 컴파일러가 바이트 코드를 컴파일하는 과정이 필요하기 때문에 초기 부팅 시간이 느릴 수 있고
컴파일 코드를 메모리에 보관하기 때문에 메모리 사용량이 증가합니다.
Q8. 런타임 시스템의 주요 기능은?
A8. 메모리 관리, 스레드 관리, 예외처리, 입출력, 네트워킹 등이 있습니다.
'🪄Interview > ✏️Study' 카테고리의 다른 글
[CS STUDY INTERVIEW] 11주차 - Error & Exception (0) | 2024.05.11 |
---|---|
[CS STUDY INTERVIEW] 10주차 - GC(Garbage Collection) (0) | 2024.05.04 |
[CS STUDY INTERVIEW] 8주차 - 락(LOCK) (0) | 2024.04.12 |
[CS STUDY INTERVIEW] 7주차 - 스레드(Threads) (0) | 2024.04.05 |
[CS STUDY INTERVIEW] 6주차 - 캐스팅 (Casting) (0) | 2024.03.25 |