PostCover

JVM 해체 분석기 - 1편, (JDK, JRE, JVM)

JDK / JRE / JVM에 대해 알아보자

Java를 처음 설치하면 JDK와 JRE가 각각 설치된다. 그렇다면 이 JDK와 JRE의 역할은 무엇인지, 그리고 JVM은 어떻게 동작하는지 정리해보자.

JDK (Java Development Kit)

JDK는 자바 개발킷의 약자로 개발자들이 자바로 개발하는데 사용되는 SDK 킷이라 생각하면 된다.

그래서 JDK안에는 자바를 개발할 때 필요한 라이브러리들과 javac, javadoc 등의 개발 도구들을 포함하고 있고, 개발을 하기 위해서는 자바 프로그램을 실행도 시켜줘야 하기 때문에 이후에 나올 JRE(Java Runtime Environment)도 함께 포함되어 있다.

SDK SDK는 Software Development Kit으로, OS 또는 프로그래밍 언어 제작사게 제공하는 도구이다.

아래 그림과 같이 JDK는 JVM, JRE를 모두 포함하고, 이외에 자바를 개발하는데 필요한 개발 도구들도 포함되어 있다.

자바의 버전을 표기할 때는 보통 JDK 또는 Java SE 버전으로 나타낸다.

초기버전에서는 버전 정보를 통해 표기하였으나, 여러 과정을 거쳐 Java SE(Java Standard Edition)로 변경되었다.

  • Java SE(Java Standard Edition): 가장 기본이 되는 표준 에디션의 자바 플랫폼으로 자바 언어의 핵심 기능을 제공한다.
  • Java EE(Java Enterprise Edition): 대규모 기업용 에디션으로 SE의 확장판이다.
  • Java ME(Java Micro Edition): 피쳐폰, 프린터와 같이 작은 임베디드 기기들을 다루는데 사용하는 에디션이다.
  • JavaFX: 가벼운 GUI를 제공하는 에디션

정리해보면, Java의 버전을 표현할 때 Java 17이라고 하는 것은 JDK의 버전을 의미한다. 따라서 Java를 설치한다는 것은 JDK를 설치하는 것과 같다.

간혹 보면 LTS라고 표기된 버전이 존재하는데 이는 Long Term Support의 약자로 장기지원 서비스를 받을 수 있는 버전을 의미한다.

자바는 워낙 유명한 프로그래밍 언이이기 떄문에 JDK도 여러가지 종류로 나뉜다. 가장 큰 이유는 라이센스 때문이다.

자바의 소스코드 자체는 오픈소스이다. 그래서 오픈소스인 자바 소스코드를 빌드하여 사용하는 것에는 거의 제약이 없지만, Oracle에서 제공하는 설치버전의 JDK에는 NFTC 라이센스가 적용되어 유료 플랜으로 사용해야 한다.

그래서 무료 라이센스를 제공하는 또 다른 JDK 버전을 여러 회사에서 출시했고, 지금의 JDK 환경이 갖춰졌다.

JDK 디렉토리의 구성 요소로는 크게 bin, include, lib으로 이뤄져있다.

~/Library/Java/JavaVirtualMachines/corretto-17.0.11/Contents/Home  ls
ADDITIONAL_LICENSE_INFO LICENSE                 bin                     conf                    jmods                   lib                     release
ASSEMBLY_EXCEPTION      README.md               commitId.txt            include                 legal                   man                     version.txt
  • bin: 자바 개발, 실행에 필요한 도구와 유틸리티 명령의 집합
  • include: 네이티브 코드 프로그래밍에 필요한 C언어 헤더 파일
  • lib: 실행 시간에 필요한 라이브러리 클래스들

bin 디렉토리에는 자바 컴파일러인 javac, 자바 인터프리터 등이 포함되어 있다.

JRE (Java Runtime Environment)

JRE는 자바 실행환경의 약자로, JVM과 자바 프로그램을 실행시킬때 필요한 라이브러리 API를 함께 묶어서 배포되는 패키지이다. 이외에도 자바 런타임 환경에서 사용하는 프로퍼티 세팅이나 리소스 파일(jar 파일)을 가지고 있다.

JRE는 기본적으로 JDK에 포함되어 있기 때문에 JDK를 설치하면 함께 설치된다. 기존에는 개별적으로 설치가 가능했지만, JDK11 버전부터는 따로 제공되지 않는다.

정리해보면, Java로 프로그램을 직접 개발하기 위해서는 JDK가 필요하고, 컴파일된 Java 프로그램을 실행시키려면 JRE가 필요하다.

JVM (Java Virtual Machine)

JVM은 자바 가상머신의 약자로, 직역하면 자바를 실행하는 기계인데, 자바를 구동하는 프로그램으로 이해하면 쉽다.

자바로 작성된 모든 프로그램은 JVM에서만 실행될 수 있으므로, 자바 프로그램을 실행하기 위해서는 반드시 JVM이 설치되어 있어야 한다.

앞서 언급했듯, JVM은 JRE에 포함되어 있다. 그래서 현재 사용하는 컴퓨터의 OS에 맞는 JRE가 설치되어 있다면 JVM 또한 설치되어 있다.

많은 사람들이 Java를 사용하는 핵심 이유중 하나가 JVM 때문이라고 해도 과언이 아닌데, JVM을 사용함으로써 얻는 가장 큰 이점은 JVM을 사용하면 자바 프로그램을 모든 플랫폼에서 제약없이 동작하도록 할 수 있다는 점이다.

아래로 이에대해 알아보자

왜 JVM?

Java는 OS에 종속적이지 않다는 특징을 가지고 있다.

OS에 종속받지 않고 실행되기 위해서는 OS 위에서 Java를 실행시킬 무언가가 필요한데, 그게 바로 JVM을 실행할 수 있게 하는 가상 컴퓨터이다.

OS에 종속받지 않는 것이 무슨 의미인지 기존 C언어와의 비교를 통해 알아보자.

C언어의 실행 (WOCA)

보통 학부 과정에서 가장 처음 배우는 프로그래밍 언어는 C언어일 것이다. C언어 코드를 작성하고 저장하고 실행하면 exe 파일 혹은 바이너리 파일로 컴파일되고, 해당 파일을 실행하면 c언어 코드가 실행된 결과를 얻는 경험은 다들 해봤을 것이다.

이렇게 소스 코드를 작성하면, 기계에 맞춰진 컴파일러가 각 기계가 이해하는 기계어로 변환해줘 실행 프로그램으로 만들어 준다.

일허게 컴파일된 코드를 Binary Code(기계어)라고 부른다.

이를 WOCA의 특성이라 부르며, Write Once, Compile Anywhere, 한 번만 작성하고 컴파일하면 어디서든 사용 가능하다를 의미한다.

그런데 이 기계어는 특정 OS나 CPU 구조에 맞추진 컴파일러에 의해 다르게 컴파일이 된다는 특징이 존재한다. 크게 구분하자면 학부의 컴퓨터 구조 시간에 RISC 머신과 CISC 머신을 들어보긴 했을 것이다. 이런 차이는 서로 같은 소스코드를 통해 컴파일을 하더라도, 각각의 머신에서는 정상적으로 동작하지 않을 수 있다.

컴파일러는 같은 형식의 기계어를 만들어내지만, 각 환경에 맞춰진 독자적인 컴파일러이기 때문에 다른 환경에서는 이해하지 못하기 때문이다.

그래서 C언어를 배울때의 특징으로 이식성이 낮다고 표현하기도 한다. 보통 C언어를 OS에 종속된 언어라고 표현하는데, 윈도우OS 환경에서 컴파일된 코드가 리눅스 환경에서는 읽어질 수 없기 때문에, 각각의 프로그램을 만들어야 한다. 따라서 OS 환경에 종속 되었다고 표현한다.

Java의 실행 (WORA)

이렇게 컴파일러가 운영체제마다 의존적이었던 문제를 해결하기 위해서 Java(JVM)이 등장하게 되었다.

Java로 작성한 소스 파일은 직접 운영체제로 가서 실행하는 것이 아닌, JVM을 거쳐 운영체제와 상호작용 하게된다. 따라서 JVM 위에서라면 운영체제로부터 독립적으로 프로그램을 제약없이 실행할 수 있다는 의미이다.

이것이 가능한 이유는 컴파일된 코드와 하드웨어/OS 사이에서, 해당 환경에 맞게 JVM이 Byte Code로 변환해주기 때문이다.

C언어에서의 컴파일 과정은 소스 코드를 Binary Code로 변환하여 하드웨어에 의해 읽어지고, 따라서 OS 환경에 따라 코드가 달라져야 했던 것과 달리, Java는 아래의 과정을 거치는 것을 통해 코드의 수정이 불필요하게 된다.

  1. Java Compiler가 Java로 작성된 소스 코드 .java 파일을 .class 파일인 Byte Code로 컴파일한다. 이때, 해당 코드는 JVM이 이해할 수 있는 코드이다.
  2. Byte Code를 기계어로 변환시키기 위해 가상 CPU가 필요한데, 이것이 JVM의 역할이다.
  3. JVM이 Byte Code를 Binary Code로 변환한다.
  4. JVM에 의해 컴파일된 기계어는 바로 CPU에서 실행된다.

이처럼 Java는 WORA의 특성으로 불리며, Write Once, Read Anywhere이라는 재컴파일할 필요 없고 바로 기계가 읽고 실행할 수 있게 한다는 의미로 사용된다.

이로 인해 이식성이 높은 특성을 갖게 되었다.

그러나, 자바 프로그램과 달리 JVM은 운영체제에 종속적이므로, 각 운영체제에 맞는 JVM을 설치해야 한다.

이러한 이식성으로 인해 Java 외에도 다른 언어도 이 JVM을 사용하고 있다.

그러나 단점도 존재한다.

자바 프로그램은 일반 프로그램보다 JVM을 통해 변환하는 과정을 더 거쳐야 하기 때문에, 상대적으로 실행 속도가 느리다는 단점이 존재한다.

즉, Java는 C언어와 달리 두 번의 컴파일로 인한 속도의 문제가 발생하는데, 이를 보완하기 위해 JIT 컴파일러라는 내부 프로그램을 사용해서 필요한 부분만을 기계어로 바꿈으로 성능 향상을 의도했지만, C언어의 실행 속도를 따라잡지는 못했다.

JIT 컴파일러 (Just-In-Time compiler) 이는 같은 코드를 매번 해석하지 않고, 실행할 때 컴파일을 하며 해당 코드를 캐싱한다. 이후에는 바뀐 부분만 컴파일하고, 나머지는 캐싱된 코드를 사용한다.