https://github.com/whiteship/live-study
백기선님 자바 기초 스터디 1주차
목표
자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.
학습할 것
- JVM이란 무엇인가
- 컴파일 하는 방법
- 실행하는 방법
- 바이트코드란 무엇인가
- JIT 컴파일러란 무엇이며 어떻게 동작하는지
- JVM 구성 요소
- JDK와 JRE의 차이
JVM이란 무엇인가
JVM (Java Virtual Machine)은 직역하면 '자바를 실행하기 위한 가상 기계'라고 할 수 있다. 영어권에서는 컴퓨터를 머신(Machine)이라고 부르기 때문에 '가상 컴퓨터'라고 이해하면 좋을 것이다.
'가상 컴퓨터'는 실제 컴퓨터(하드웨어)가 아닌 소프트웨어로 구현된 컴퓨터라는 뜻으로 컴퓨터 속의 컴퓨터라고 생각하면 된다.
자바로 작성된 애플리케이션은 모두 이 가상 컴퓨터(JVM)에서만 실행되기 때문에, 자바 애플리케이션이 실행되기 위해서는 반드시 JVM이 필요하다.
- 일반 애플리케이션 -> OS -> 컴퓨터(하드웨어)
- Java 애플리케이션 -> JVM -> OS -> 컴퓨터(하드웨어)
Java는 코드는 JVM을 거쳐서 OS로 전달되기 때문에 속도가 느리다는 단점을 가지고 있다. 그러나 요즘엔 바이트코드(컴파일된 자바코드)를 하드웨어의 기계어로 바로 변환해주는 JIT 컴파일러와 향상된 최적화 기술이 적용되어서 속도의 격차를 많이 줄였다. (해당 내용은 아래서 다루겠다)
JAVA는 JVM하고만 상호작용을 하기 때문에 OS와 하드웨어에 독립적이다. 즉, 다른 OS에서도 프로그램의 변경없이 실행이 가능하다. 단, JVM은 OS에 종속적이기 때문에 해당 OS에서 실행가능한 JVM이 필요하다.
자바의 개발사인 썬에서는 여러 운영체제에 설치할 수 있는 서로 다른 버전의 JVM을 제공하고 있다.
-> 쉽게 말해 자바를 실행시켜주는 기계라고 생각하면 된다.
컴파일 하는 방법
자바를 설치하면 기본적으로 여러 실행파일들을 제공한다. 주요 실행파일들은 다음과 같다.
- javac.exe: 자바 컴파일러, 자바소스코드를 바이트코드로 컴파일한다.
- java.exe: 자바 인터프리터, 컴파일러가 생성한 바이트코드를 해석하고 실행한다.
다음 예제를 보자.
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world.");
}
}
이 예제를 실행하려면, 먼저 자바컴파일러(javac.exe)를 사용해서 소스파일(Hello.java)로부터 클래스파일(Hello.class)을 생성해야 한다. 그 다음에 자바 인터프리터(java.exe)로 실행한다.
Hello.java -> Hello.class -> "Hello, wrold." 출력
실행 창(cmd)을 켜서 컴파일을 해보자.
Hello.java가 있는 폴더로 이동해서 javac Hello.java를 실행한다.
javac.exe 컴파일러가 Hello.class 파일을 생성한다.
javac 옵션
<classpath>
컴파일러가 컴파일 하기 위해서 필요로 하는 참조할 클래스 파일을 찾기 위해서 컴파일시 파일 경로를 지정해주는 옵션
예를 들어, Hello.java파일이 C:\Java 디렉터리에 존재하고, 필요한 클래스 파일들이 C:\Java\Engclasses에 위치한다면, javac -classpath C:\Java\Engclasses C:\Java\Hello.java 로 해주면 된다.
<target>
지정된 자바 버전의 JVM에서 작동 되어지도록 클래스파일을 생성시킨다.
ex) JVM 1.2 버전에서 호환되어 질수 있는 클래스 파일 생성
javac -target 1.2 Hello.java
->
자바 상위 버전의 클래스 파일은 하위 버전에서 실행할 수 없다.
그러나 하위 버전의 클래스 파일은 상위 버전에서 실행할 수 있다.
대신 상위 버전에서 컴파일 버전 옵션을 사용하면 하위 버전에서 실행 가능하다.
실행하는 방법
이제 Hello.class 파일을 실행해보자.
java Hello를 실행하면 java.exe 인터프리터가 Hello.class 파일을 실행한다.
-> 원하는 결과인 Hello, world가 출력된 것을 볼 수 있다.
바이트코드란 무엇인가
- javac.exe: 자바 컴파일러, 자바소스코드를 바이트코드로 컴파일한다.
여기서 바이트코드란 무엇일까?
바이트코드(Bytecode)는 특정 하드웨어가 아닌 가상 컴퓨터(VM)에서 돌아가는 실행 프로그램을 위한 이진 표현법이다. 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에, 보통 기계어보다 더 추상적이다.
역사적으로 바이트코드는 대부분의 명령 집합이 0개 이상의 매개 변수를 갖는 1바이트 크기의 명령 코드(opcode)였기 때문에 바이트코드라 불리게 되었다
(https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C)
말이 어려운데 이를 자바의 경우로 풀어서 생각해보자.
자바 바이트코드는 JVM이 실행하는 명령어 집합이다. 컴파일하면 생성되는 .class 파일이 바이트코드를 담고 있다.
JVM 즉, 소프트웨어가 처리하기 때문에 0과 1로 구성된 기계어가 아닌 보다 추상적인 형태를 띄고 있다.
바이트코드에는 256개가량의 opcode들이 존재한다. -> 이 opcode들이 한 바이트로 이루어져 있다.
따라서 바이트코드라 불리게 되었다.
앞서 javac Hello.java를 수행했을 때, Hello.class 바이트코드 파일이 생성되는 것을 확인했다.
바로 이 Hello.class 파일이 바이트코드 명령어로 구성되어 있다.
-> C로 치면 어셈블리 코드와 같다고 보면 된다. (사실 이 말이 제일 이해하기 쉽다)
+) 바이트코드는 javap [클래스 파일] 명령으로 볼 수 있다.
JIT 컴파일러란 무엇이며 어떻게 동작하는지
JIT 컴파일러란?
JIT 컴파일(just-in-time compilation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다.
자바 가상 머신(JVM)은 JIT 컴파일러를 지원한다. 즉, 자바 컴파일러가 자바 프로그램 코드를 바이트코드로 변환한 다음, 실제 바이트코드를 실행하는 시점에서 JVM이 바이트코드를 JIT 컴파일을 통해 기계어로 변환한다.
JIT 컴파일러 동작 방식
컴퓨터 프로그램을 만드는 방법은 크게 두 가지가 있다.
1. 인터프리트 방식과
2. 정적 컴파일 방식으로 나눌 수 있다.
인터프리트 방식은 실행 중 프로그래밍 언어를 한 줄 한 줄 읽어가며 해당 기능에 대응하는 기계어 코드를 실행한다.
정적 컴파일은 반면에 실행하기 전에 프로그램 코드를 기계어로 번역한다.
자바는 두 가지의 방식을 혼합한 방식을 사용한다.
JIT 컴파일러는 자주 사용하는 코드의 경우 미리 기계어로 컴파일한 뒤 캐싱해둔다.
JVM은 실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 자주 사용하는 코드는 JIT 컴파일러로 미리 캐싱해 놓은 코드를 사용해 빠르게 실행하게 된다.
JVM 구성 요소
JVM은 크게 3가지 요소로 구성되어 있다.
- 클래스 로더
- 실행 엔진
- 런타임 데이터 영역
클래스 로더
자바 애플리케이션은 하나 또는 여러 개의 클래스로 구성되어 있다. 자바 컴파일시 하나의 애플리케이션에서 클래스 별로 .class 파일이 생성되고 이러한 여러 개의 .class 파일을 JVM 내로 로드시켜야 한다.
클래스 로더가 바로 이런 기능을 수행하는 JVM의 구성요소이다.
클래스 로더는 런타임에 클래스를 JVM 메모리(런타임 데이터 영역)에 로딩한다. 이 때, 모든 클래스 파일을 한 번에 메모리에 올리는 것이 아니라 필요할 때마다 동적으로 올려준다.
클래스 로더는 로딩(클래스 읽기) -> 링킹(레퍼런스 연결) -> 초기화의 단계로 동작한다.
실행 엔진
클래스로더에 의해 런타임 데이터 영역에 적재된 클래스(바이트코드)를 기계어로 변환하고 실행하는 역할을 한다.
명령어는 인터프리터 방식으로 하나하나 실행되기도 하고, JIT 컴파일러에 의해 적절한 시간에 전체 바이트코드를 네이티브 코드로 변환하여 실행되기도 한다.
실행 엔진의 또 한가지 요소로 가비지 컬렉터가 있다.
가비지 컬렉터는 힙 영역에 생성되어 있는 객체들 중 사용되지 않는 객체를 찾아 제거하는 역할을 한다.
런타임 데이터 영역
JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.
이 영역은 크게 메소드, 힙, 스택, PC 레지스터, 네이티브 메소드 스택 영역으로 구성되어 있다.
메소드 영역
클래스 정보가 저장되는 공간이다.
클래스의 멤버 변수의 이름과 타입, 메소드의 이름과 파라미터, 리턴값이 저장되고, 각종 상수, static 메소드, final 클래스 변수 등이 저장된다.
이 공간에는 Runtime Constant Pool이라는 별도의 관리 영역이 있는데, 여기에 상수 자료형을 저장하여 중복을 막는 역할을 한다.
힙 영역
new 키워드에 의해 생성되는 클래스와 배열 등이 저장된다.
메소드 영역에 저장된 클래스만이 생성 가능하고, 가비지 컬렉터에 의해 사용되지 않은 클래스/배열이 제거된다.
스택 영역
지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값 등이 생성되는 영역이다. int와 같은 변수들이 이곳에 저장된다.
특히 Person p = new Person() 같이 클래스를 생성할 경우 new에 의해 생성된 클래스는 힙 영역에 저장되고,
클래스의 참조인 p는 스택 영역에 저장된다.
PC 레지스터
스레드가 생성될 때마다 생성되며, 현재 실행중인 주소와 명령을 저장한다.
멀티 스레드가 동작할 때, 이곳의 정보를 이용해 여러 스레드를 동시에 실행할 수 있다.
네이티브 메소드 스택
자바가 아닌 다른 언어로 작성된 코드를 실행할 때 사용하는 공간이다.
즉, JVM 위에서 자바 코드가 아닌 C, C++, 어셈블리 같은 다른 언어들로 작성된 라이브러리들을 호출하거나 반대로 호출되는 것을 가능하게 한다.
JDK와 JRE의 차이
JRE
JRE(Java Runtime Environment)는 자바 프로그램을 실행시켜주는 환경을 구성해주는 도구다.
JRE는 JVM의 실행환경을 구현했다고 할 수 있다.
하지만 자바 프로그래밍 도구는 포함되어 있지 않기 때문에 자바 프로그래밍을 위해서는 JDK가 필요하다.
+) 자바 9버전 이후로 JRE는 따로 만들지 않는다. -> 이미 JDK에 다 포함되어 있음
JDK
JDK(Java Development Kit)는 자바 개발시 필요한 툴킷을 제공하는 도구모음이다.
JDK를 설치하면 JRE도 같이 설치가 되기 때문에 JDK = JRE + @ 라고 생각하면 된다.
'java > java' 카테고리의 다른 글
[Java] 배열의 출력 (0) | 2021.06.23 |
---|---|
[Java] 자바 데이터 타입, 변수 그리고 배열 (1) | 2021.06.22 |
[Java] javac - error: error while writing 오류 (0) | 2021.06.12 |
[Java] 'javac'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. (0) | 2021.06.12 |
[Java] Java 환경 변수 설정 - Window (0) | 2021.06.12 |