-
JVM 메모리 구조Java 2023. 3. 9. 20:05
JVM은 Java Virtual Machine의 약자로, 자바 가상 머신이라고 부르며 자바 프로그램 실행환경을 만들어 주는 소프트웨어다.
자바 코드를 컴파일하여 .class 바이트 코드로 만들면 이 코드가 자바 가상 머신 환경에서 실행된다.
자바와 운영체제 사이에서 중개자 역할을 수행하며, 자바가 운영체제에 구애 받지 않고 프로그램을 실행할 수 있도록 도와준다.
또한, 가비지 컬렉터를 사용한 메모리 관리도 자동으로 수행하며, 스택 기반으로 동작한다.
JVM을 사용함으로써 얻는 가장 큰 이점은 JVM을 사용하면 하나의 바이트 코드(.class)로 모든 플랫폼에서 동작하도록 할 수 있다는 점이다.
C언어로 작성된 Test.c를 윈도우 컴파일러를 사용하여 컴파일하면 Test.exe가 만들어진다.
윈도우 컴파일러로 컴파일 되었기 때문에 Test.exe는 윈도우에서만 실행되는 파일이다. -> 리눅스 운영체제에서는 실행할 수 없다.
C, C++ 에서는 컴파일 플랫폼과 실행 플랫폼이 다를 경우 프로그램이 정상적으로 동작하지 않는다.
Java언어로 작성된 Test.java를 컴파일하면 Test.class 파일이 생성된다. 생성된 바이트 코드는 각자의 플랫폼에 설치되어 있는 자바 가상 머신(JVM)이 운영체제에 맞는 실행 파일로 바꿔준다.
즉, 하나의 바이트코드로 JVM이 설치되어 있는 모든 플랫폼에서 동작이 가능하다.
자바 프로그램의 동작 방식
1. 자바로 개발된 프로그램을 실행하면 JVM은 OS로 부터 메모리를 할당한다.
2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일한다.
3. Class Loader를 통해 JVM Runtime Data Area로 로딩한다.
4. Runtime data Area에 로딩 된 .class 들은 Execution Engine을 통해 해석한다.
5. 해석된 바이트코드는 Runtime Data Area의 각 영역에 배치되어 수행하며 이 과정에서 Execution Engine에 의해 GC의 작동과 스레드 동기화가 이루어진다.
JVM의 구조
클래스 로더 (Class Loaer)
자바는 동적으로 클래스를 읽어오게 되어 프로그램이 실행 중인 런타임에서야 모든 코드가 자바 가상 머신과 연결된다.
이렇게 동적으로 클래스를 로딩해주는 역할을 하는 것이 클래스 로더(Class Loader)이다.
.java 파일이 생성되고 .java소스를 컴파일하면 .class파일이 생성되는데, 클래스 로더는 .class파일을 묶어서 JVM이 운영체제로부터 할당받은 메모리 영역인 Runtime Data Area로 적재한다.
실행 엔진(Execution Engine)
클래스 로더에 의해 JVM으로 로드된 .class 파일들이 Runtime Data Area의 Method Area에 배치된 이후, JVM은 Method Area의 바이트 코드를 실행 엔진(Execution Engine)에 제공하여, 바이트 코드를 실행시킨다.
이떄, 로드된 바이트 코드를 실행하는 런타임 모듈이 실행 엔진(Execution Engine)이다.
가비지 컬렉터(Garbage Collector)
JVM은 가비지 컬렉터를 이용하여 더는 사용하지 않는 메모리를 자동으로 회수해준다.
개발자가 따로 메모리를 관리하지 않아도 되므로, 손쉽게 프로그래밍을 할 수 있도록 도와준다.
Heap 메모리 영역에 생성된 객체들 중에서 참조되지 않은 객체들을 탐색 후 제거하는 역할을 한다. 언제 실행되는 지 정확한 시간은 알 수 없다.
GC 역할을 수행하는 스레드를 제외한 나머지 모든 스레드들은 일시정지 상태가 된다.
런타임 데이터 영역(Runtime Data Area)
JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역
모든 스레드가 공유해서 사용 (GC의 대상)
- 힙 영역
- 메서드 영역
스레드 마다 하나씩 생성
- 스택 영역
- PC 레지스터
- 네이티브 메서드 스택
힙 영역(Heap Area)
- new 키워드로 생성된 객체와 배열이 생성되는 영역
- 주기적으로 GC가 제거하는 영역
힙 영역은 크게 Young Generation 영역과 Old Generation 영역으로 구성되어 있고, Young Generation은 다시 Eden과 두개의 Survivor Space 로 구성되어 있다.
Young Generation
생명주기가 짧은 "젊은 객체"를 GC 대상으로 하는 영역이다.
두개의 Servivor space가 존재
해당 영역에서 발생되는 GC를 Minor GC라고 하며 Major BC에 비해 속도가 빠르다.
Old Generation
생명주기가 긴 "오래된 객체"를 GC 대상으로 하는 영역이다.
해당 영역에서 발생되는 GC를 Minor GC라고 하며 Minor GC에 비해 속도가 느리다.
JVM의 Heap 영역 메모리 동작 과정
1. 최초의 객체는 Young Generation의 Eden에서 생성된다.
2. 이후 Eden이 Full GC가 한 번 발생한 후 살아남은 객체는 Survivor 영역 중 From Space로 이동한다.
3. 다시 Eden이 GC가 발생하면 From Space의 객체는 To Space로 이동하게 된다.
4. 마지막으로 이 과정이 반복되면서 객체가 여전히 살아있는 상태라면 Old Generation 영역으로 이동하게 된다.
스택 영역(Stack Area)
지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값 등이 생성되는 영역
PC 레지스터 (PC Register)
Thread가 생성될 때마다 생성되는 영역으로 현재 스레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역
네이티브 메서드 스택(Native Method Stack)
자바 이외의 언어(C, C++, 어셈블리 등)으로 작성된 네이티브 코드를 실행할 때 사용되는 메모리 영역으로 일반적인 C 스택을 사용한다.
참고
'Java' 카테고리의 다른 글
Thread와 Runnable (1) 2023.04.11 Java 메모리 누수(Memory Leak), GC 튜닝 (0) 2023.03.30 [Effective Java] wait와 notify보다는 동시성 유틸리티를 애용하라 (0) 2023.02.14 [Effective Java] 스레드보다는 실행자, 태스크, 스트림을 애용하라 (0) 2023.02.14 [Effective Java] 필요 없는 검사 예외 사용은 피하라 (0) 2023.02.06