개요
여러 언어를 다뤄보니 기술의 원리를 알아야 해결되는 문제들을 여러 번 만난 적이 있습니다. 당시에는 구글링으로 해결되면 넘어갔지만 따로 공부가 필요하다고 생각이 들어서 하나하나 정리해보겠습니다.
JVM(Java Virtual Mahcine) 란?
Java Virtual Machine의 약자로 바이트 코드로 컴파일된 자바를 실행시켜주는 가상 머신입니다.
자바 실행 환경 (java 컴파일 과정)
1) 자바 프로그램이 실행되면, JVM이 OS로부터 메모리를 할당 받음
2) 자바 컴파일러에 의해 자바 바이트 코드(. class)로 변환
3) 변경된 파일들을 class loader를 통해 JVM 메모리 영역(Runtime Data Area)으로 로딩함
- class loader : 런타임 시 클래스들을 로딩시키며, 메모리에 로드함
4) 로딩된 class 파일들을 해석
5) 해석된 바이트 코드는 메모리 영역에 배치되어 수행됨
⇒ 이러한 과정을 거치며, JVM은 필요에 따라 스레드 동기화, 가비지 컬렉션 같은 메모리 관리 작업 수행
* 바이트 코드란?
0과 1로 구성되어 있는 이진 코드이지만 바이너리 코드와 다르게 가상 머신(JVM)이 이해할 수 있는 코드
JVM의 구조
JVM은 바이트 코드를 실행하기 위해 3 단계를 수행합니다.
- 클래스 로더 하위 시스템
- 런타임 데이터 영역
- 실행 엔진.
1. 클래스 로더 시스템
클래스 로더의 임무는 클래스를 JVM으로 로드하는 것입니다.
런타임 시점에 클래스를 로딩하게 해 주고 클래스의 인스턴스가 생성되면 클래스 로더를 통하여 메모리에 적재됩니다.
클래스를 로드하기 위해 "로드-> 링크-> 클래스 파일 초기화"를 수행합니다.
2. 런타임 데이터 영역
JVM이 프로그램을 실행하기 위해서 OS로부터 할당받은 메모리 공간
아래 이미지 메모리에서 볼 수 있듯이 5 개의 구성 요소가 있습니다.
Method area
모든 스레드가 공유하는 메모리 영역으로 클래스, 인터페이스, 메서드, 필드, static 변수 등의 바이트 코드를 저장.
JVM은 프로그램 실행을 위해 하나의 메서드 영역만 생성합니다.
(클래스 로더 참조도 메서드 영역에 저장됩니다. 이전 Java에는 정적 풀을 저장하기 위한 perm gen 공간이 있지만 새 Java는 이러한 세부 사항을 저장하기 위해 메타 공간을 사용합니다.)
Heap
힙 영역에는 모든 개체 참조가 저장됩니다. 힙에는 참조 유형 데이터가 있습니다. 문자열과 배열을 포함하는 모든 개체 데이터는 힙 영역에 저장됩니다. 힙 영역도 JVM 당 한 번 생성됩니다.
Stack
지역 변수 데이터는 메서드 당 프레임으로 저장됩니다. 각 프레임에는 로컬 변수 데이터를 포함할 메서드에 대한 데이터가 포함됩니다. 따라서 메서드가 완료될 때마다 프레임이 스택에서 튀어나옵니다.
이것은 LIFO (Last In First Out)를 사용합니다. 활성 스레드마다 새 스택이 생성됩니다.
PC Register
스레드에 따라 다른 스택이 생성되고 작업 순서가 PC Register에 저장됩니다.
Native Method Stack (5. 네이티브 메서드 라이브러리)
자바 이외의 다른 언어로 작성된(C++, C)등의 코드를 위한 Stack(JNI)
3. 실행 엔진
클래스 로더에 의해서 메모리에 적재되었으면 이제 인터프리터를 사용하여 바이트 코드를 변환하여 실행해야 합니다.
실행 엔진의 내부적으로는 인터프리터, JIT 컴파일러, GC 가 존재합니다.
인터프리터
바이트 코드를 한 줄씩 기계 코드로 변환 한 다음 순차적으로 실행하는 역할.
한 줄씩 해석하여 읽기 때문에 느리다는 평가를 받음.
이 문제를 극복하기 위해 JIT 컴파일러는 JVM 인터프리터와 병렬로 사용됩니다.
JIT(Just In Time) 컴파일러
JVM 런타임에서 바이트 코드를 원시 기계 코드로 컴파일합니다.
JVM은 바이트 코드의 어느 부분이 더 자주 실행되는지 모니터링하며, 자주 사용되는 바이트 코드를 네이티브 코드로 변환하여 실행 속도를 높이기로 결정합니다. JIT로 컴파일된 원시 코드는 코드 캐시에 저장됩니다.
자주 사용되는 바이트 코드를 네이티브 코드로 컴파일하는 프로세스는 별도의 스레드에서 수행됩니다. 처음에 JVM은 계속해서 바이트 코드를 사용하고 JIT 컴파일이 준비되면 원시 코드를 사용합니다.
즉, 프로그램을 실행할 때 인터프리터가 번역을 하여 사용하지만 반복적으로 나오는 코드의 경우는 매번 작업을 하는 것보다 최초 1회 번역하여 특정 저장소에 저장하여 참조하는 방식으로 성능 향상을 이끌어 낼 수 있다.
GC(Garbage Collector)
백그라운드에서 실행되는 데몬 스레드 (낮은 우선순위 스레드)입니다.
힙 영역에서 사용되지 않는 개체를 확인하고 힙에서 제거하여 메모리 공간을 확보합니다.
해당 객체가 클래스에서 참조되지 않는 경우 객체는 가비지 수집 대상입니다.
즉, 객체 값이 null이면 가비지 수집에 적합합니다. 가비지 수집기에는 finalize ()라는 메서드도 포함되어 있습니다. 이 메서드에는 정리를 위해 프로그래머가 재정의 할 수 있는 빈 구현이 있습니다.
기타. JNI (Java Native Interface)
C 및 C ++와 같은 언어로 작성된 Native Libraries에 액세스 하기 위한 Execution Engine 용 인터페이스
마무리
그냥 썼을때는 몰랐었는데, 공부하다 보니 생각보다 중요한 개념인 거 같았습니다. 최적화나 알 수 없는 에러가 났을 때 왜 났는지? 다른 방식으로도 고민해볼 수 있는 선택지가 하나 오픈된 거 같다는 생각이 드네요.
내용이 틀린 부분이나 이상한 부분이 있을 수도 있으니 주의하면서 참고하시면 좋을 거 같습니다.
이번 포스팅은 마무리하며 다음 포스팅에서 뵙겠습니다.
참고)
https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-1.html#jvms-1.1
https://papago.naver.net/website?
https://en.wikipedia.org/wiki/Java_virtual_machine
https://javatutorial.net/jvm-explained
https://www.notion.so/java-JVM-1238f12eccff4b67be263cd4a5b7dbbe
https://ravikugan-r.medium.com/introduction-to-jvm-architecture-3b157d859863
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
오버 로딩(Overloading)과 오버 라이딩(Overriding) (0) | 2021.07.03 |
---|---|
Call by Value와 Call by Reference 란? (0) | 2021.06.29 |
객체 지향 프로그래밍(OOP)란? (1) | 2021.06.22 |
GC(Garbage Collector) 란? (0) | 2021.06.19 |
[Java] 자바 설치 및 환경 설정하기 (0) | 2021.06.13 |