자바를 제대로 공부해보고 싶어 어떻게 공부하는 것이 좋을지 찾아보던 중 백기선님의 라이브 스터디를 봤다. 이미 해당 레파지토리에는 스터디 끝낸 사람들이 후기까지 적고 있지만 각 주차마다 목표와 학습 방향이 잘 제시되어 있어 해보기로 마음 먹었다.
목표
자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기
학습할 것
- JVM이란 무엇인가
- 컴파일 하는 방법
- 실행하는 방법
- 바이트코드란 무엇인가
- JIT 컴파일러란 무엇이며 어떻게 동작하는지
- JVM 구성 요소
- JDK와 JRE 차이
자바 언어의 특징
-
운영체제에 독립적
기존 언어는 개발된 프로그램을 다른 운영체제에 적용하기 위해 많은 노력이 필요한 반면, 자바는 그렇지 않다. 자바 응용프로그램은 운영체제나 하드웨어가 아닌JVM
하고만 통신하기 때문이다. JVM이 자바 응용프로그램으로부터 받은 명령을 해당 운영체제가 이해할 수 있게끔 알아서 변환해준다. 따라서 자바는 운영체제에는 독립적이지만 JVM에는 종속적이라고 볼 수 있다. -
객체지향 언어
자바는 상속, 캡슐화, 다형성을 잘 적용한 순수 객체지향 언어로 재사용성이 높고 유지보수에 용이하다. -
자동 메모리 관리 (Garbage Collection)
자바 프로그램을 실행하면 가비지컬렉터가 자동적으로 메모리를 관리해주기 때문에 개발자는 따로 메모리를 관리하지 않아도 된다. -
기타
- 네트워크와 분산처리 지원
- 멀티쓰레드 지원
- 동적 로딩을 지원
- 실행 시 모든 클래스가 로딩되지 않고, 필요한 시점에 클래스를 로딩하여 사용한다.
JVM이란 무엇인가
JVM은 Java Virtual Machine의 약자로 자바를 실행하기 위한 가상 머신이다. 가상 머신은 실제 하드웨어가 아닌 소프트웨어로 구현된 하드웨어인데, 쉽게 컴퓨터 속의 컴퓨터라고 생각하면 된다.
(사진 출처 - 네이버 블로그)
위 사진에서 일반 프로그램은 OS를 거치면 바로 하드웨어에 전달된다. 반면 자바 프로그램은 JVM을 한 번 더 거치고, 실행 시 interpret 되기 때문에 속도가 다소 느리다. 하지만 요즘엔 JIT 컴파일러가 통해 바이트코드를 기계어로 바로 변환해주는 덕에 속도 격차를 많이 줄이긴 했다.
일반 프로그램은 OS 종속적이므로 다른 OS에서 실행시키기 위해서는 애플리케이션을 해당 OS에 맞게 변경해야 한다. 반면 자바 애플리케이션은 OS에 독립적이고, JVM하고만 상호작용한다. 따라서 Windows, Linux, MacOS 등 어떠한 OS에서도 프로그램 변경없이 실행 가능하다. 물론 JVM이 OS에 종속적이기 때문에 해당 OS에서 실행 가능한 JVM이 필요하긴 하다. 이로써 자바의 장점 중의 하나인 "Write once, run anywhere"가 가능하다.
컴파일 및 실행하는 방법
자바로 프로그래밍 하기 위해서는 JDK (Java Development Kit)를 설치해야 한다. JDK를 설치하면 JVM과 Java API 외 필요한 프로그램들이 설치된다. 설치를 완료하고 bin 디렉토리에 가면 주요 실행 파일들이 있다.
- javac.exe : 자바 컴파일러, 자바 소스코드를 바이트코드로 컴파일
- java.exe : 자바 인터프리터, 바이트코드를 해석하고 실행
- javap.exe : 역어셈블러, 컴파일된 클래스 파일을 원래 소스로 변환
// Hello.java
class Hello {
public static void main(String[] args) {
System.out.println("Hello, world !");
}
}
Hello.java 파일을 실행하는 순서는 1) 자바 컴파일러를 이용해 소스파일(.java)로부터 클래스 파일(.class)을 생성하고, 2) 자바 인터프리터로 실행하는 것이다.
$ javac Hello.java
$ java Hello
바이트코드란 무엇인가
자바 컴파일러에 의해 소스파일이 바이트코드(.class)로 변환되는데, 이는 컴퓨터가 바로 인식할 수 없다. C언어를 생각해보면 소스파일이 오브젝트 파일로 변환될 때 0과 1로만 이루어진 바이너리 코드로 변환된다. 즉, 컴파일 후 이미 컴퓨터가 읽을 수 있는 코드가 되는 것인데 자바는 그렇지 않다.
다시 말해 바이트코드는 JVM이 이해할 수 있는 언어이다. 컴퓨터가 바로 읽게끔 해도 되는데 왜 JVM만 읽을 수 있게 만들었을까? 그 이유는 위에서 계속 언급했듯, 운영체제 종류에 상관없이 JVM만 있으면 실행되게 하기 위해서이다.
JIT 컴파일러란 무엇이며 어떻게 동작하는지
JIT 컴파일러에서 JIT는 Just-In-Time의 약자이다. 소스코드를 컴파일해서 바이트코드로 변환하고, 자바 인터프리터가 이 바이트코드를 기계어로 해석하고 실행한다. 이 작업은 비용이 많이 들기 때문에 이러한 단점을 극복하기 위해 나온 것이 JIT
방식이다.
JIT 컴파일러는 같은 코드를 매번 해석하는 대신 자주 쓸만한 코드를 컴파일해두고 사용한다. 이로써 인터프리터의 느린 실행 속도를 개선해 줄 수 있다. 하지만 장점만 있는 것은 아니다. 초기 실행 속도나 메모리를 잡아두는 부분에서는 약간 손해를 본다.
JVM 구성 요소
(출처 - 티스토리)
- Class Loader
- 자바 컴파일러에 의해 변환된 바이트코드를 메모리에 로드한다.
- Execution Engine
- Class Loader가 Runtime Data Areas에 불러온 바이트코드를 실행하는데, 인터프리터 방식과 JIT 방식이 있다.
- Garbage Collector
- 코드가 실행되어 객체가 생성되면 메모리를 사용하고, 코드가 종료되어 더이상 쓰이지 않는 객체를 garbage라고 한다. C/C++에서는 malloc 후 free 를 통해 개발자가 직접 메모리 해제를 해줘야하지만, 자바에서는 가비지 컬렉터가 자동으로 메모리 관리를 해준다.
- Runtime Data Areas
- 프로그램을 실행하기 위해 운영체제로부터 할당받은 공간으로 5가지 영역으로 나뉜다.
- Method area : JVM이 시작될 때 생성되어 모든 쓰레드가 공유하는 영역이다.
- Heap : new 연산자로 생성된 객체를 저장하는 공간으로 가비지 컬렉터의 대상이다.
- Stack : 쓰레드마다 각각 하나씩 존재하며 지역변수, 매개변수 등 method 수행 중 발생하는 임시데이터를 저장한다.
- PC register : 쓰레드가 현재 수행중인 명령어의 주소를 갖는다.
- Native method : 자바 외의 다른 언어로 작성된 프로그램을 실행시키기 위한 영역이다.
JDK와 JRE 차이
JDK (Java Development Kit)는 자바를 개발하기 위한 환경으로 JRE를 포함한다. JRE (Java Runtime Environment)는 자바를 실행시키기 위한 환경으로 JVM을 포함한다.