[자바 스터디] 1주차. JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가
20 Nov 2020
Reading time ~5 minutes
- 1. JVM이란 무엇인가
- 2. 컴파일 하는 방법
- 3. 바이트코드란 무엇인가
- 4. JIT 컴파일러란 무엇이며 어떻게 동작하는지
- 5. JVM 구성요소
- 6. JDK와 JRE의 차이
- 7. javac 옵션
1. JVM이란 무엇인가
JVM(Java Virtual Machine, 자바 가상 머신)이란 자바로 작성된 소스코드를 운영체제에 독립적으로 실행할 수 있도록 해주는 가상 컴퓨터(프로그램)입니다. 실제 컴퓨터와 마찬가지로 명령어 세트가 있고 다양한 메모리 영역을 런타임에 조작할 수 있습니다. 자바 언어로 작성된 소스코드(.java)를 실행시키기 위해서는 기계가 이해할 수 있는 언어로 변환하는 과정을 거쳐야 합니다. 이러한 과정인 컴파일 과정을 거치면 클래스 파일(.class)이 만들어지게 됩니다. 이 클래스 파일을 실행하는 역할을 해주는 것이 바로 JVM입니다.
2. 컴파일 하는 방법
JDK에 포함되어있는 javac 라는 프로그램을 사용하여 자바 파일을 컴파일을 할 수 있습니다. IDE1나 텍스트 편집기 등을 통해 아래와 같은 자바 소스 파일을 작성하고 저장합니다.
public class FirstJavaProgram{
public static void main(String[] args){
System.out.println("Hello First Java Program");
int intTypeValue1 = 10;
int intTypeValue2 = 100;
System.out.print("intTypeValue1 + intTypeValue2 = ");
System.out.println(intTypeValue1 + intTypeValue2);
}
}
그 다음 콘솔창(명령프롬프트,터미널 등)에서 다음 명령들을 통해 작성된 자바 파일이 있는 디렉토리(폴더)로 이동한 후 javac 프로그램명.java 명령을 실행하여 컴파일할 수 있습니다.
- 디렉토리 변경 명령어
'cd 디렉토리명' (하위 디렉토리로 진입할 때)
'cd ..' (상위 디렉토리로 빠져나올 때)
- 컴파일 명령어
아래와 같이 사용하며 한 개 이상의 소스파일을 컴파일 할 수 있습니다.
javac HelloWorld.java (디렉토리 내 HelloWorld.java 파일 한 개 컴파일)
javac Hello*.java (디렉토리 내 Hello로 시작하는 모든 자바 소스 파일들을 컴파일)
javac *.java (디렉토리 내 모든 자바 소스 파일들을 컴파일)
- 실행 명령어
java 프로그램명 (컴파일된 클래스파일을 실행할 때)
컴파일 후 해당 디렉토리에 프로그램명.class파일이 생성된 것으로 확인할 수 있습니다.
3. 바이트코드란 무엇인가
자바 가상 머신이 이해할 수 있는 클래스 파일(.class)내의 소스코드를 의미합니다. 명령코드(opcode)들의 크기가 1바이트라서 붙여진 이름입니다. 자바 뿐만 아니라 안드로이드 개발에 많이 사용되는 코틀린과 같은 자바와 유사한 다른 언어들도 바이트코드로 변환되어 자바 가상 머신에서 실행할 수 있습니다.
위에 작성된 FirstJavaProgram.java 파일을 javac 명령을 통해 컴파일하면 FirstJavaProgram.class 파일이 생성됩니다. 이 클래스파일을 javap -c 명령으로 역어셈블하면 사람이 이해할 수 있는 명령코드(opcode)를 확인할 수 있습니다.
4. JIT 컴파일러란 무엇이며 어떻게 동작하는지
자바 프로그램은 실행중인 런타임에 기계가 이해할 수 있는 언어로 변환하는 과정을 거칩니다. JIT 컴파일러(Just-In-Time Compiler)란 자주 실행되거나 반복되는 코드의 변환 내용을 캐싱하여 기계어로 변환할 필요가 없이 빠르게 실행하도록 도와주는 역할을 합니다.
5. JVM 구성요소
1) Class Loader
클래스 파일에서 클래스 이름, 타입, 메소드, 멤버 변수 등의 클래스 관련 정보를 읽어 런타임에 동적으로 메모리에 로드하는 역할을 합니다.
2) Execution Engine
(1) GC (Garbage Collector)
주소를 잃어버린 메모리에서 더 이상 사용하지 않는 메모리를 해제하는 역할을 합니다.
(2) JIT Compiler (Just-In-Time Compiler)
런타임에 자주 사용되는 코드의 변환 내용을 캐싱하여 더욱 빠르게 실행할 수 있게 도와주는 역할을 합니다.
3) Runtime Data Area
(1) Method Area
런타임 상수 풀, 필드 및 메서드 데이터와 같은 클래스 별 구조 등 메서드 및 생성자에 대한 코드를 저장하는 영역입니다.
(2) Heap
모든 클래스 인스턴스 및 배열에 대한 메모리가 할당되는 영역입니다. 프리미티브 타입이 아닌 데이터들이 저장되는 영역입니다. GC에 의해 메모리가 관리됩니다.
(3) Stack
메소드가 호출되면 지역변수와 함께 할당되는 영역입니다. 프리미티브 타입의 데이터는 스택에 생성됩니다. 스레드가 생성될 경우 각 스레드마다 하나의 스택을 할당 받게 됩니다.
(4) Program Counter Registers
현재 실행중인 JVM 명령어의 주소를 가리키는 역할을 합니다.
(5) Native Method Stack Area
자바 이외의 다른 언어로 작성된 소스코드를 위한 영역입니다. JNI(Java Native Interface)를 통해 다른 언어의 코드를 수행하기 위해 존재합니다.
6. JDK와 JRE의 차이
- JDK(Java Development ToolKit, 자바 개발 도구)
JDK는 애플리케이션 개발에 필요하거나 유용한 컴파일러 및 디버거와 같은 JRE와 커맨드라인 개발도구가 포함되어 있습니다.
- JRE(Java Runtime Environment, 자바 런타임 환경)
JRE은 자바 프로그래밍 언어로 작성된 애플리케이션을 실행하는데 필요한 라이브러리, 자바 가상 머신 및 기타 구성요소를 제공합니다. JDK, JRE, JVM등의 포함관계를 그림으로 나타내면 아래와 같습니다.
7. javac 옵션
javac <option> <source files>
-
javac -help
javac의 옵션과 옵션의 사용목적을 알려줍니다. -
javac -classpath <path> 또는 javac -cp <path>
컴파일러가 컴파일 할 클래스 파일의 경로를 지정해주는 옵션입니다. 여기서 path는 절대 경로2를 의미합니다. -
javac -d <directory>
클래스 파일을 생성할 디렉토리를 지정합니다. 기본값은 현재 자바 소스파일이 존재하는 위치 즉, 제 경우에는 Desktop 입니다. Desktop의 하위 디렉토리로 JavaStudy라는 빈 폴더를 만들어 보았습니다. 처음의 ls 명령으로 확인할 수 있듯이 어떤 파일도 없는 빈 폴더입니다. 그 후 javac -d JavaStudy FirstJavaProgram.java 명령으로 다른 디렉토리인 JavaStudy 폴더에 FirstJavaProgram.class라는 컴파일된 클래스파일을 생성할 수 있습니다. -
javac -source <release>
javac -source release 옵션을 사용하여 소스 파일에 적용된 Java 버전을 컴파일러에 알릴 수 있습니다. javac -source 1.8과 같이 사용합니다. -
javac -target <release>
javac -target release 옵션을 사용하면 컴파일러는 특정 Java 가상 머신 (VM) 버전에 대한 .class 파일을 생성할 수 있습니다. javac -target 1.8과 같이 사용합니다. -
javac -g
모든 디버깅 정보를 생성합니다. 라인 수와 지역 변수, 소스 파일 정보 등의 디버깅 정보는 javap -l FistJavaProgram 명령으로 확인 할 수 있습니다. -g:lines,vars,source와 동일한 명령입니다. -g를 붙이지 않은 기본값은 -g:lines,source와 동일하게 컴파일됩니다.
-
javac -g:[lines,vars,source]
:[lines,vars,source]를 붙여 사용하면 라인 수 정보, 지역 변수, 소스 파일 정보 등 일부 정보를 디버깅 정보로 생성할 수 있습니다.
javac -g:lines나 javac -g:lines,source FirstJavaProgram.java와 같이 사용합니다.
-
javac -g:none
:none을 붙여서 사용하면 모든 디버깅 정보를 생성하지 않습니다.
javap -l FistJavaProgram 명령을 실행해보면 javac -g 명령과 달리 LineNumberTable과 LocalVariableTable이 생성되지 않은 것을 확인 할 수 있습니다.