목표

자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.

학습할 것

  • JVM이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성 요소
  • JDK와 JRE의 차이

공부 내용

1. JVM이란 무엇인가?

정의: JVM은 Java Virtual Machine의 줄임말로 어느 환경(Mac, Windows, Linux)이던지 자바 프로그램을 실행할 수 있도록 도와주는 프로그램

출처: https://www.infoworld.com/article/3272244/what-is-the-jvm-introducing-the-java-virtual-machine.html

왜 나왔을까? : 기존에 개발자들이 많이 사용하던 C언어의 어려움인 메모리를 직접 관리하는 부분과 절차지향적인 부분을 탈피해 객체지향을 표방하기 위해서 나왔을거라고 생각됨 (개인적 추측)

장점: JVM만 설치 되어 있으면 윈도우든 리눅스든 OS에 종속적이지 않고 자바 코드를 실행 가능함
           Garbage Collection이 있어서 개발자가 메모리 할당, 해제를 신경 쓰지 않아도 됨
           (단, 성능에 크리티컬한 부분에서는 단점이 될 수 있음)

기본적인 실행흐름은 .java 파일 작성 -> class파일로 컴파일 -> class파일을 JVM에서 실행한다. 상세한 실행흐름은 2번에서 확인

 

2. 컴파일 하는 방법

javac fileName.java 수행하면 fileName.class이 만들어진다.

javac fileName.java

출처: https://www.guru99.com/java-virtual-machine-jvm.html#1

 

주요 javac 옵션은 아래와 같다.

옵션 설명 예제
-classpath, -cp 클래스패스, 즉 실행할 클래스의 위치를 지정한다. javac -cp "/Users/home/A.java"
-d 어디에 클래스파일을 생성할지 지정한다. javac -d "/User/home/path"
-encoding 소스 파일에 사용된 인코딩을 지정한다. javac -encoding "uft-8" A.java
-g 모든 디버깅 정보를 출력 javac -g ~~~
-verbose

컴파일러가 진행하는 작업을 모두 출력한다.

javac -verbose ~~~
-sourcepath 소스파일 위치 지정 javac -sourcepath "/User/home/path"
-source 소스파일 자바 버전 지정 javac -source 1.8 ~~~
-target 타겟파일 자바 버전 지정 javac -target 1.8 ~~~

 

3. 실행하는 방법

java fileName.class

위 명령어로 실행할 때 JVM의 구성요소인 클래스로더가 fileName.class 파일을 메모리상의 JVM으로 가져온다.

내부적으로는 classLoader -> Byte Code Verifier (바이트코드 변조 확인) -> Execution Engine에서 실행되는 구조다.
Execution Engine에서 클래스파일(바이트코드로 구성)을 기계어로 변경해서 명령어 단위로 실행한다.
다만 명령어 단위 실행은 2가지 방식으로 동작한다.

3-1. Interpreter 방식: 명령어를 하나씩 수행 하는 방식 (삼겹살 3줄이 있다고 하면, 1줄 굽고 먹고 1줄 굽고 먹고 하는 방식으로 이해)

3-2. JIT(Just In Time compiler) 방식: 전체 바이트코드를 네이티브 코드로 변환하고 그 이후에는  네이티브 코드로 실행하는 방식 (삼겹살 3줄 다 구워놓고 먹는 방식으로 이해하면 편하다)

 

4. 바이트코드란 무엇인가

자바 가상 머신이 이해할 수 있는 언어로 변환된 자바 소스 코드, 자바 컴파일러로 변환되는 코드의 명령어 크기가 1바이트라서 바이트코드라고 불린다고 한다.

다시 풀어서 이해하자면 자바는 OS에 종속적이지 않기 위해서 JVM 이 이해할 수 있는 언어인 바이트코드 형태로 제공되고 바이트코드와 JVM이 있으면 어느 운영체제에 종속적이지 않고 실행된다고 이해하면 된다.
(실제로 .java 파일은 확장자가 java인 단순 텍스트 파일임)

출처: http://tcpschool.com/java/java_intro_programming

 

5. JIT 컴파일러란 무엇이며 어떻게 동작하는가?
JIT 컴파일러란? 기존 클래스파일(바이트코드)를 실행하는 방법은 Interpreter 방식이 기본이다. Interpreter 방식은 3-1에서 설명한바와 같이 명령어를 하나씩 해석해서 처리하는 개념이기 때문에 명령어 하나하나 실행하는 속도는 빠를지 모르나 전체 코드 관점에서 보면 실행 속도가 느린 단점이 있다. 해당 문제를 해결하기 위해서 나온 방법이 JIT 컴파일러이고
JIT 컴파일러는 런타임 시 클래스파일(바이트코드)를 네이티브 기계어로 한방에 컴파일 후 사용하는 개념으로 이해하는 것이 편하다.

좀 더 상세히 얘기하자면 전체 컴파일 후 캐싱 -> 이후 변경된 부분만 컴파일하고 나머지는 캐시에서 가져다가 바로 실행 이다. 바로 꺼내서 사용하고 변경 부분만 컴파일 하기 때문에 코드 수행속도가 Interpreter 방식에 비해서 빠르다!

출처: https://aboullaite.me/understanding-jit-compiler-just-in-time-compiler/

 

JIT 컴파일러의 내부구조는 아래와 같으며 JIT 컴파일러는 바이트코드를 일단 중간 단계의 표현인 IR(Intermediate Representation)로 변환하여 최적화를 수행하고 그 다음에 네이티브 코드를 생성한다.

출처: https://www.cubrid.org/layouts/layout_master/img/a3a96a6fa297c5a591e4f40090ae66de.png

JVM도 종류가 여러가지로 나뉘는데(오라클, IBM 등등) 여기서는 오라클 핫스팟 VM에 대해서만 보고 가려고 한다.
오라클 핫스팟 VM은 코드를 분석한 후 컴파일이 필요한 부분만 JIT 컴파일러를 통해서 컴파일하고 나머지는 인터프리터 방식으로 동작한다.
그리고 컴파일된 코드라도 자주 호출되지 않는다면 네이티비 코드(컴파일된 코드)에서 제거하고 해당 부분은 인터프리터 방식으로 동작한다.

6. JVM 구성요소

출처: https://www.guru99.com/images/1/2.png

6-1. Class Loader: 클래스 로더는 클래스 파일을 로드하는 데 사용되는 하위 시스템이다.


6-2. Method Area: JVM Method Area는 메타데이터, 상수 런타임 풀, 메서드에 대한 코드와 같은 클래스 구조를 저장한다.
                               공유자원(여기서 공유자원이라는 의미는 다른 스레드에서도 활용 가능한 자원을 말함)이다.


6-3. Heap:
모든 개체, 관련 인스턴스 변수 및 배열은 힙에 저장된다.
                   이 메모리는 여러 스레드에 걸쳐 공유된다.


6-4. JVM Language Stacks: Java Language Stacks는 로컬 변수를 저장하고 부분적인 결과를 얻는다.
각 스레드에는 자체 JVM 스택이 있으며, 스레드가 생성될 때 동시에 생성된다.
메서드를 호출할 때마다 새 프레임이 생성되고, 메서드 호출 프로세스가 완료되면 삭제된다.
스택은 공유자원이 아니므로 스레드 세이프(여러 스레드에서 공용 자원을 접근할 때 생길 수 있는 문제) 하다.
내부에는 Local Variable Array, Operand Stack, Frame Data의 영역이 있다.


6-5. PC Register: PC 레지스터는 현재 실행 중인 Java 가상 시스템 명령의 주소를 저장한다.
자바에서는 각 스레드에 별도의 PC 레지스터가 있다.

6-6. Native Method Stack: 네이티브 메서드 스택은 네이티브 라이브러리에 따라 네이티브 코드 명령을 보관한다.
자바 대신 다른 언어로 쓰여 있다.


6-7. Execution Engine: 런타임 데이터 영역에 할당 된 바이트코드는 실행 엔진에 의해 실행된다.
실행 엔진은 바이트코드를 읽고 조각 별로 실행한다.


6-8. Native Method Interface: Native Method Interface는 프로그래밍 프레임워크다. JVM에서 실행 중인 Java 코드가 라이브러리 및 네이티브 애플리케이션으로 호출할 수 있도록 한다.


6-9. Native Method Libraries: Native Libraries는 실행 엔진에 필요한 Native Libraries(C, C++)의 모음이다.

 

7. JDK와 JRE의 차이

출처: https://www.guru99.com/difference-between-jdk-jre-jvm.html

JRE는 자바 어플리케이션을 수행하기 위한 SW(JVM과 Class Libraries등 최소한의 환경)라고 볼 수 있고 JDK는 JRE + 개발에 필요한 SW를 모아 놓은 좀 더 큰 범위의 SW라고 볼 수 있다.
따라서 자바 어플리케이션을 개발하기 위해서는(컴파일, 디버깅, Doc 문서 생성) JDK를 필수로 설치 해야 한다.

 

 

참고. JVM 전체 아키텍처 그림

출처: https://t1.daumcdn.net/cfile/tistory/250D6A4B592F5FE41B

+ Recent posts