1. JAVA의 컴파일과 실행
JAVA의 컴파일과 실행과정을 살펴보면 JVM을 이해하는데 도움이 될 것이다.
컴퓨터는 우리가 작성한 소스코드를 이해하지 못한다. 컴퓨터가 이해할 수 있는 언어는 기계어이며 0과 1로 이루어져 있다. 그래서 우리가 작성한 소스코드를 컴퓨터가 이해할 수 있는 기계어로 변환해야 한다.
이미지 출처: preamtree
하지만 자바에서는 소스코드가 바로 기계어(바이너리코드)로 컴파일되지 않는다. 일차적으로 소스코드(. java)가 자바 컴파일러인 javac에 의해 JVM 에서 해석되는 중간 코드인 바이트 코드(. class)로 컴파일된다.
** 바이너리코드 vs. 바이트 코드 혼동주의!
그다음 링크 과정을 거친다. 링크란, 여러 개로 분리된 소스파일들을 최종 실행 가능한 파일로 만들기 위해 서로 연결해 주는 작업이다. 자바 프로그램 실행 도중, JVM이 필요한 클래스를 찾아서 클래스 패스(Class Loader에 의해)에 로드해준다.
그리고 JVM 내의 Execution Engine에 의해 바이트 코드가 기계어로 바뀐다. 이때 인터프리터, JIT 컴파일러 두 가지 방식이 있다.
1-1. 자바는 하이브리드 언어이다.
자바는 다른 컴파일 언어들이 작동하듯이 컴파일러(javac)를 이용해 전체 코드를 한 번에 번역한다. 이때 번역된 코드를 바이트 코드라고 하며 바이트 코드는 JVM의 자바 인터프리터를 이용해 한 줄씩 실행된다. 이는 인터프리터 언어의 특성이다. 따라서 자바는 컴파일언어와 인터프리터 언어의 특성을 모두 가지고 있는 하이브리드 언어라고 할 수 있다.
2. JVM 이란 무엇인가?
JVM은 Java Virtual Machine의 약자로 자바를 실행시키기 위한 가상 머신을 말한다. 머신이라는 말이 어색하지만, 자바 소스코드를 실행시키기 위해 컴퓨터 속에 존재하는 또 다른 컴퓨터 정도로 이해하면 된다. 위에서 보았듯, 우리가 자바로 작성한 소스코드(.java)는 Java Byte Code(.class) 로 변환되어 JVM 에 의해 해석되고 실행된다.
자바의 가장 큰 장점은 운영체제에 독립적이라는 것이다. JVM 덕분에 자바는 어떤 운영체제 위에서도 실행 파일의 변경 없이 동일한 모습으로 실행 가능하다.
C 언어는 운영체제에 따라 다른 목적 파일(기계어)을 생성하지만, 자바는 목적 파일(바이트코드) 를 만들고 실제 물리적인 컴퓨터 내에 자바 언어가 운영체제에 따라 영향을 받지 않도록 JVM 을 이용하여 가상 컴퓨터 환경을 구축하고 그곳에 목적 파일을 던져 자바 코드를 실행시킨다.
자바 코드는 컴퓨터에서 바로 실행한, 완전히 컴파일된 상태가 아니고 실행 시 JVM을 통해 해석되기 때문에 속도가 느리다는 단점이 있다.요즘엔 바이트코드를 기계어로 바로 변환해주는 JIT 컴파일러와 향상된 최적화 기술이 적용되어 속도 문제는 상당히 개선되었다.
3. JVM 구성 요소
JVM은 크게 3가지 구성요소를 가진다.
3-1. Class Loader
자바 클래스들은 시작 시 한번에 로드되지 않고, 애플리케이션에서 필요할 때 로드된다.
클래스 로더는 런타임에 클래스를 동적으로 JVM에 로드 하는 역할을 수행한다. 자바의 클래스들은 자바 프로세스가 새로 초기화되면 클래스로더가 차례차례 로딩되며 작동한다.
3-2. Runtime Data Area
JVM이 프로그램을 실행하기 위해 운영체제로부터 할당받은 메모리 공간이다.
클래스 로더가 배치한 데이터들을 보관하는 저장소로 다섯 가지의 구성요소로 나뉘어져 있다.
- Method Area, Heap Area, Stack Area, PC Registers, Native Method Stacks
- Stack , PC, Native Method Stacks는 쓰레드마다 하나씩 생성되며 그 외의 메모리 영역은 모든 쓰레드가 공유한다.
중요하다고 생각하는 2가지만 먼저 살펴보려고 한다.
(1) Stack
호출된 메소드와 메소드 정보가 저장되는 영역이다. 메소드가 실행될 때마다 매개변수, 로컬 변수, 복귀 주소 등이 저장된다. 이러한 저장을 스택 프레임이라고 부르기도 한다. 메소드 종료시 메모리 공간이 소멸된다.
(2) Heap
런타임시 동적으로 메모리가 할당되고 소멸되는 영역이며 GC(Garbage Collection)을 수행하는 영역이다.
3-3. Execution Engine
실행 엔진은 바이트코드를 읽고 실행하는 역할을 한다. 바이트 코드를 실행하는 방식에는 인터프리터, JIT 두 가지 방식이 있다.
1. 인터프리터
인터프리터 방식이란 자바 바이트 코드를 한 줄씩 읽고 해석하는 방식을 말한다. 하나씩 해석하고 처리하기 때문에 전체 코드의 관점에서 봤을 때 속도가 느리다는 단점이 있다.
반면, 소스코드가 수정될 때마다 재컴파일해줘야 하는 정적 컴파일 방식(C, C++)과 달리 인터프리터 방식은 컴파일 과정을 거칠 필요 없이 수정이 쉽다는 장점이 있다.
2. JIT 컴파일러
JIT(Just-In-Time) 컴파일러는 인터프리터 방식의 단점인 속도 문제를 해결하기 위해 나온 인터프리터와 컴파일러를 결합한 방식이다.
처음엔 인터프리터 방식을 사용하다가 적정한 때에 바이트코드 전체를 기계어로 바꾼다. 인터프리터 방식으로 바이트코드를 기계어로 번역할 때 중복된 코드를 캐싱하여 똑같은 코드를 매번 번역하는 것을 방지해 속도를 보완해준다.
(1) Gargabe Collector
JVM이 메모리를 관리하는 방법
동적으로 할당된 Heap 메모리 영역에서 더 이상 참조하지 않아 필요 없는 영역을 해제한다.
지금까지 JVM 에 대해 알아보았다. 하나하나 세세하게 알아보진 않았지만 대략적인 JVM 의 내용을 알아보았다. 이후에 Runtime Data Area 와 Gargabe Collector 에 대해 자세히 다루는 포스팅을 해보려고 한다.
참고
- [Java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가 https://velog.io/@dsunni/Java-JVM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B8%EA%B0%80
- [Java] JVM의 클래스 로더란? https://steady-coding.tistory.com/593
- 자바의 가장 큰 장점 - 운영체제에 독립적이다 https://jaeseongdev.github.io/development/2021/03/08/%EC%9E%90%EB%B0%94%EC%9D%98_%EA%B0%80%EC%9E%A5_%ED%81%B0_%EC%9E%A5%EC%A0%90_%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%97%90_%EB%8F%85%EB%A6%BD%EC%A0%81%EC%9D%B4%EB%8B%A4/
'java' 카테고리의 다른 글
Hash와 HashMap에 대한 이해 (0) | 2023.08.20 |
---|