목차
1. JVM이란 무엇인가?
2. 컴파일 하는 방법
3. 실행하는 방법
4. 바이트코드란 무엇인가?
5. JIT 컴파일러란 무엇이며 어떻게 동작하는가?
6. JVM 구성요소
7. JDK와 JRE의 차이
자바가상기계를 뜻하는 JVM(Java Virtual Machine)을 가장 담백하게 설명하자면 '자바 프로그램을 실행시켜주는 프로그램' 그것이 바로 JVM이다.
물론, JVM이 가지는 특성들을 이해할 필요는 있다. 그렇다면 JVM이 가지는 특성은 무엇인가?
JVM 을 통해 우리는 자바 프로그램을 OS나 장비에 구애받지 않고 사용할 수 있게 된다. 1995 년 자바가 릴리즈 될 당시에는 모든 컴퓨터 프로그램은 특정 운영체제에 맞춰 작성되었다. 같은 프로그램이더라도 Window용, Linux용, 등등 운영체제에 맞게끔 프로그래밍을 해야하는 불편함이 있었다. 하지만 JVM 은 운영체제 위에서 작동하며 자바 프로그램과 운영체제 사이에서 일종의 인터페이스 역할을 수행해 자바 프로그램이 운영체제나 디바이스에 종속되지 않도록 하였다.
자바가 생겨나기 이전의 모든 프로그램의 메모리는 프로그래머가 관리해야만 했다. 하지만 자바 프로그램에서 이러한 메모리관리는 JVM이 도맡아서 해준다.
메모리 관리는 Garbage Collection 이라는 프로세스를 통해 설명할 수 있는데, 간단히 Garbage Collection의 매커니즘을 설명하자면 프로그램에서 더 이상 사용되지 않는 부분을 찾아내 메모리에서 제거함으로써 메모리 관리를 하는 프로세스이다. 어떻게 사용되지 않는 부분을 찾느냐가 궁금할 수 있을텐데, 그 부분은 심화적인 내용임으로 찾아서 공부할 필요가 있다.
컴파일 하는 방법을 알기 이전에 컴파일이 무엇인지에 대해 간단히 알아보자.
컴파일이란 고급언어로 작성된 소스코드를 컴퓨터가 이해할 수 있도록 기계어로 변환하는 과정을 의미한다.
조금더 직관적으로 설명하자면 .java 파일을 .class 파일로 변환하는 과정을 의미한다.
대부분의 프로그래머들이 IDE를 사용하기 때문에 직접 컴파일을 해야할 일은 드물 것이다. 하지만, 직접 컴파일 하는 것을 몰라서 나쁠게 있겠는가? 직접 컴파일 해보도록 하자.( 물론 여기서 직접 한다는 것은 소스코드를 기계어로 번역하는 일을 하겠다는 것은 아니다. 그냥 직접 java comiler를 실행해보자는 뜻이다.) 컴파일을 위해선 컴파일 작업을 수행할 java compiler가 필요하다. java comiler는 jdk를 설치하면 bin 폴더안에 javac 라는 이름으로 설치된다.
컴파일을 직접 수행하기 위해 간단 자바 소스코드를 에디터로 작성해보았다.
자바소스코드 임으로 저장할 때, .java 를 명시해두자.
그리고 터미널에서 컴파일한 소스코드가 있는 디렉토리로 이동한 후,
javac [컴파일할 소스코드파일] 을 쓰고 엔터를 누르면 컴파일이 완료된다.
위와같이 .class 파일이 생성된 것을 확인할 수 있다.
생성된 .class 파일을 실행하는 방법은 간단하다. 해당 클래스파일 있는 디렉토리로 이동한 상태에서 java [실행할 클래스파일] 을 쓰고 엔터를 누르면 실행이 된다.
위에서 컴파일이란 고급언어를 기계어로 변환시키는 작업을 의미한다고 설명하였다. 그런데 사실 javac 를 통해 고급언어를 컴파일하면 기계어가 아닌 JVM이 이해할 수 있는 바이트코드로 변환시키는 것을 의미한다. 왜 그렇다면 이 작업을 컴파일 한다고 하였을까? 그 뜻을 이해하기 위해 Virtual Machine 에 대한 이해를 조금 해보자. 일반적으로 컴퓨팅에서 '기계(Machine)'은 코드를 실행하는 하드웨어를 뜻한다. 그런데 '가상 머신'이라는 것은 하드웨어가 아닌 소프트웨어임을 그 이름에서 내포하고있다. 즉, JVM은 코드를 실행하는 소프트웨어이지만 어쨋든 그 이름에 Machine 이 붙어있고 이 JVM이라는 기계가 실행하는 코드가 바이트코드임으로 고급언어를 바이트코드로 변환시키는 작업을 컴파일이라 칭하게 되었다.
JIT 는 Just-In-Time 을 뜻하는 말로, 그 시작은 사실 일본 도요타라는 회사 에서 생산 시스템 내에서 시간을 단축하고 공급 업체와 고객의 응답 시간을 줄이는 것을 목표로하는 방법론을 의미하는 말이다. 여기서 주목해야할 키워드는 '시간을 단축, 응답 시간을 줄임' 이다.
키워드를 기억하면서 살펴보자, 자바 프로그램을 실행하기 위해선 소스코드를 바이트코드로 변환시키는 컴파일러 뿐만 아니라 바이트코드가 특정 환경의 기계에서 실행될 수 있게끔 기계어로 변환하는 인터프리터도 필요하다. 그런데 인터프리터의 기본적인 매커니즘은 번역할 코드를 한 줄 한 줄 번역해 나가는 것이다. 이러한 매커니즘으로 인해 동일한 바이트코드가 있더라도 또 번역을 해야하는 단점이 있었다. 하지만 JIT에서는 이러한 단점을 보완하기 위해 번역한 코드를 캐싱해 두었다가 동일한 코드가 있을 경우 일일이 번역 작업을 하지않고 캐싱해둔 값을 사용함으로써 인터프리팅 시간을 단축시킬 수 있게 되었다.
그렇다면 왜 자바에서는 컴파일러와 인터프리터 두 가지를 병행하는 것일까에 대한 궁금증이 생길 수 있는데 이와 관련한 좋은 글이 있어 링크를 달아 두겠다.
자바의 컴파일러와 인터프리터
JVM의 구성요소는 크게 4가지이다.
그것은 바로 NativeMethodInterface, ExcutionEngine, Runtime Data Areas, ClassLoader SubSystem 인데, 무엇인지 살펴보도록 하자
Runtime Data Area는 JVM이 운영체제로 부터 할당받은 메모리를 위 그림과 같은 5가지 영역으로 구분지어 놓은 것이다.
위 5가지 영역에서 PC registeres / Native Method Stack / Stack Area 영역은 쓰레드마다 생성이되고 Method Area / Heap Area 영역은 모든 쓰레드에서 공유되는 영역이 된다.
클래스 로더는 런타임에 참조하는 .class 파일들을 Runtime Data Area 에 로드하는 작업을 수행한다. 이 로딩과정은 순차적으로 이루어지는데
BootStrap Class Loader 가 JVM을 실행하기 위한 핵심 클래스들을 로딩하고 Extension Class Loader 가 JAVA의 확장 클래스들을 로딩하며 System Class Loader 가 우리가 작성한 .class 파일을 로딩한다고 생각하면 된다.
EXcutionEngine(실행엔진)은 클래스 로더로부터 런타임 데이터 영역에 배치된 바이트코드를 실행하는 역할을 한다. 바로 여기서 위에서 설명한 JIT 컴파일러가 필요에 따라 동작하게 된다.
또한 더 이상 사용되지 않는 인스턴스들을 삭제하는 GC도 포함한다.
실행 엔진에 필요한 원시 라이브러리의 모음을 뜻한다.
이부분에 대해서는 잘 알지 못한다.
JRE 는 Java Runtime Environment 로 자바 실행 환경을 의미한다.
자바 프로그램을 실행시키기 위해 필요한 JVM 과 JVM을 동작시키기 위한 라이브러리를 포함하게 된다.
JDK 는 Java Development Kit 말 그대로 자바로 프로그램 개발을 하기위한 모든 파일을 포함한 kit 를 의미한다. 자바를 실행하기 위핸 JRE도 JDK의 일부이고 컴파일러나 그 외에 다양한 자바 개발을 위해 필요한 파일들이 포함되어 있다.
[도움을 얻은 곳]
https://www.infoworld.com/article/3272244/what-is-the-jvm-introducing-the-java-virtual-machine.html
http://www.freetimestudy.com/Features_of_ava.php
https://www.eclipse.org/openj9/docs/jit/
스터디 GITHUB 주소