[java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가
목차
- 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 가 있다.
- Interpreter : 기존 바이트 코드를 실행하는 방법은 인터프리터 방식이 기본, 바이트 코드를 명령어 단위로 한줄씩 읽기 때문에 느리다.
- JIT (just-in-time) : 런타임 시점에 바이트코드를 기계어로 한번에 컴파일 후 사용한다. 아래에서 더 자세히 다루겠다.
- 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의 차이
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 까지 설치해야한다.