[java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가

2021. 11. 11. 09:56Backend/☕️ Java

목차

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

 

 


 

JVM 이란?

 

1. JVM(Java Virtual Machine)은 자바 프로젝트를 실행하기 위한 자바 가상머신이다. 

2. 이 가상머신은 자바와 운영체제 사이 중계자 역할을 하며, 자바가 OS에 상관없이 실행되도록 한다.

- 여기서 말하는 OS 란, 우리가 흔히 아는 windows, macOS 등에 해당된다. JVM 은 어떤 OS 에서도 문제없이 java 프로그램이 실행되도록 도와주는 중간 단계 역할을 맡는다.

3. 개발자가 작성한 자바 소스코드는 우선 컴파일러에 의해 바이트 코드로 컴파일 된 후, JVM 에 의해 해당 OS 가 이해할 수 있도록 기계어로 바꿔 실행된다. (컴파일러는 인간이 작성한 코드를 기계가 이해할 수 있도록 엮어주는 작업, 그리하여 바이트코드가 생성된다.)

4. 추가) Garbage Collection 을 이용하여 자동으로 메모리를 관리하는 역할도 있다.

 

 

 

 

자바 컴파일 및 실행방법

 

컴파일 하기 위해선 javac.exe, 실행하기 위해선 java.exe 프로그램이 필요하다.

 

1. 컴파일 방법

1) 자바 소스파일 작성

2) javac.exe 프로그램 사용하여 .java 파일을 컴파일

$ javac 소스파일.java

3) 컴파일 정상 완료시 바이트코드인 .class 파일 생성됨.

* 바이트 코드와 바이너리 코드는 엄연히 다르다.

 

 

2. 실행 방법

1) java.exe 파일 사용하여 컴파일 된 .class 파일 실행 

$ java 소스파일명   // 실행 시에는 확장자명 불필요

 

 

 

바이트 코드란?

 

흔히 컴퓨터는 0과 1로 이루어진 코드를 이해한다고 알려져있다. 0과 1 로 이루어진 코드를 바이너리(binary) 코드라 부른다.

여기서 착각할 수 있는 부분이, 기계어라는 단어를 바이너리 코드와 동일하다고 생각하는 점이다. 기계어가 바이너리 코드로 이루어진 것은 맞지만, 모든 이진코드가 기계어인 것은 아니다. (기계어 != 바이너리 코드)

정리를 해보면, 

 

1. 바이너리 코드

- 컴퓨터가 인식할 수 있는 0과 1로 이루어진 이진코드를 의미

 

 

2. 기계어

- 물론 0과 1로 이루어진 바이너리 코드이다.

- 하지만 기계어는 바이너리 코드로 이루어졌을 뿐이지, 모든 이진코드가 기계어 인것은 아니다.

- 즉, 이진코드와 기계어는 별개이다.

- 기계어는 특정한 언어가 아니라, CPU 제조사에서 CPU 를 만들 때 해당 칩에서 사용하는 명령어 집합을 기계어라 부른다.

 

 

3. 바이트 코드

- 컴퓨터가 이해하는(=CPU) 코드가 이진코드라면, 바이트 코드는 가상머신(ex: JVM)이 이해할 수 있는 언어다.

- 컴파일된 코드의 명령어 크기가 1바이트라서 바이트 코드라 불린다. 때문에 8비트 짜리 이진코드라 생각하면 이해하기 쉽다.

- 결국 바이트 코드는 가상머신이 이해할 수 있는 이진 표현법이라 생각하면 된다.

 

 

 

 

 

JVM 구성 요소

 

JVM 은 크게 보면, Class loader, Execution Engine, GC, Runtime Data Area 로 나뉜다.

 

1) Class Loader

- 컴파일된 .class 파일을 로드하는 JVM 의 하위 시스템이다. 

- JVM 은 OS 로부터 Runtime Data Area 라는 메모리 영역을 할당받는데, 클래스 로더가 이 메모리 영역에 클래스 파일을 적재(로드)한다.

 

여담) 자바 프로그램은 메인 메서드를 가장 먼저 실행하여 프로그램을 시작한다. 즉 클래스 로더는 main() 메서드를 가진 클래스를 가장 먼저 Runtime Data Area 에 적재한다.

 

 

2) Execution Engine

실행엔진 내부에는 인터프리터와 JIT, GC 가 있다.

  1. Interpreter : 기존 바이트 코드를 실행하는 방법은 인터프리터 방식이 기본, 바이트 코드를 명령어 단위로 한줄씩 읽기 때문에 느리다.
  2. JIT (just-in-time) : 런타임 시점에 바이트코드를 기계어로 한번에 컴파일 후 사용한다. 아래에서 더 자세히 다루겠다.
  3. GC (Garbage Collection) : 불필요한 오브젝트를 JVM이 알아서 정리해준다.

 

3) Runtime Data Area

- OS 로 부터 할당받은 메모리 공간으로 JVM 은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

- 할당받은 메모리 공간이기 때문에, 클래스 로더는 이 공간에 컴파일된 바이트 코드를 적재한다.

 

 

1. PC Register (개별 스레드 마다 보유)

- 스레드가 시작될 때 생성되는 공간으로 스레드 마다 하나씩 존재한다.

- 스레드가 어떤 명령을 실행할지 기록하는 부분

 

 

2. Stack (개별 스레드 마다 보유)

- 스레드가 생성될 때 마다 하나씩 생성된다 (스레드 마다 stack 이 생성된다는 뜻)

- 스레드 제어를 위해 사용되는 메모리 영역으로, 메서드가 호출되면 메서드와 메서드 정보는 stack 에 쌓인다(=저장된다).

- 여기서 말하는 메서드 정보란, 메서드의 매개변수, 지역변수, 메서드를 호출한 주소 등을 의미한다.

- 메서드가 종료될 때 할당 받은 메모리 공간은 사라진다.

 

 

3. Heap ( 모든 스레드간 공유 ) 

- new 연산자를 사용하여 객체가 동적으로 생성되면, 인스턴스가 heap 영역의 메모리에 할당되어 사용된다.

 

 

4. Native Method Stack

- 자바 이외의 언어에서 제공되는 method 의 정보가 저장되는 공간

 

 

5. Method Area ( 모든 스레드간 공유)

- 프로그램 실행 중 클래스가 사용되면 JVM은 해당 클래스 파일을 읽어 분석하여 클래스의 인스턴스 변수, 메서드 코드 등을 method area 에 저장한다. 클래스 변수 역시 이 영역에서 함께 생성된다.

 

 

 

 

JIT 컴파일러란 무엇이며, 어떻게 동작하는지

 

문제: 기존 인터프리터 방식은 명령어 코드(바이트 코드)를 한줄씩 연산하기 때문에 전체 코드 관점으로 볼 땐 느린다는 단점이 있다. 

해결 : 이 문제를 해결하기 위해 도입된 JIT 컴파일러는, 한줄씩 연산하던 인터프리터와 달리, 바이트 코드 전체를 컴파일하여 기계어로 변경한다.

과정: 이때 JIT 컴파일러의 컴파일 과정은 바이트 코드를 바로 기계어로 변경하는 것이 아니라, IR(intermediate representation)로 변환하여 최적화를 수행하고 그 다음에 기계어로 변환하는 과정을 거친다.

효과: 기계어는 캐시에 보관되기 때문에 한 번 컴파일된 코드는 캐시에서 바로 꺼내어 실행할 수 있고, 이로 인해 더 빠르게 프로그램을 실행할 수 있다.

 

 

 

 

 

 

 

JDK와 JRE의 차이

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

JRE는 읽기 전용, JDK는 읽기 & 쓰기 전용으로 볼 수 있다.

 

1. JDK ( Java Development Kit )    JDK = JRE + @ (개발에 필요한 도구)

JDK 는 자바 개발도구의 약자로,  자바  프로그램을 컴파일 하고 개발할 수 있도록 지원하는 개발 환경의 세트를 의미한다. 혹자는 JDK 를 Java용 SDK (Software Development Kit) 라고도 한다. 즉 자바 어플리케이션을 개발하기 위해서는 (컴파일, 디버깅, JavaDoc etc) JDK 를 필수로 설치해야한다.

 

2. JRE (Java Runtime Environment)   JRE = JVM + Libraries

JVM 과 class libraries 를 포함하는 개념으로, (Java를 개발할 때가 아닌) 자바 프로그램을 실행할 때 꼭 JRE가 필요하다. 

Environment 명칭에서 알 수 있듯 JRE 는 환경으로, Java 관련 파일이 있는 디렉터리이다.

포함된 폴더와 파일을 간략하게 살펴보면, 

  • bin/ : java 실행 프로그램과 JVM 을 시작하는 java 가 포함되어 있다.
  • conf/ : 사용자가 편집할 수 있는 구성 파일(configuration files)을 포함한다.
  • lib/ : 여러 지원파일을 포함하는데, Java 표준 라이브러리의 .class 파일을 포함하는 모듈 등이 존재한다.

 

정리하자면, 자바 어플리케이션을 개발하기 위해서는 (컴파일, 디버깅, JavaDoc etc) JDK 를 필수로 설치해야한다.

 

* JDK 와 JRE 의 짧은 결론

컴퓨터에서 자바 프로그램을 실행하는데만 초점을 둔다면 JRE 만 설치하면 된다. 반면 자바 프로그램을 개발하기 위해선 JDK 까지 설치해야한다.