티스토리 뷰
목차
배경
최근 많은 국내 IT 회사에서는 점차 JVM 기반 언어인 Kotlin을 도입하고 있다. 이번에는 Java가 처음 등장한 배경을 빌드 과정과 실행 방식을 통해 살펴보고, JVM 기반 언어란 무엇인지, 이를 배경으로 Kotlin의 동작 방식과 실행 원리는 어떠한지 살펴본다.
C++의 빌드 과정
Java의 빌드과정과 실행 원리를 살펴보기 위해서, C++의 빌드 과정을 먼저 살펴보자.
Java가 등장하기 전, C++는 각 환경별로 실행되는 컴파일러가 각각 존재했다. 운영 체제별로 서로 다른 컴파일러가 소스 코드를 읽어들이고, 운영체제에 최적화된 바이너리를 만들어 냈다. 즉, 윈도우에서는 윈도우 C++ 컴파일러인 MSVC 컴파일러가 윈도우에 최적화된 바이너리를 만든다. 리눅스에서는 리눅스 C++ 컴파일러인 GCC가 리눅스에 최적화된 바이너리를 만들어된다.
다시 말해, 운영체제별로 컴파일러가 상이하여 이식성이 좋지 않다. 운영체제별로 컴파일러가 완전히 다르기 때문에 같은 소스 코드 일지라도 다르게 동작할 가능성이 있다. 심지어 컴파일조차 되지 않는 경우도 흔했다.
Java의 빌드 과정
C++는 소스 코드의 컴파일 환경과 실행 환경이 하나로 결합되어 있었다. 이로 인해 각 환경별로 서로 다른 컴파일러를 제공했고, 소스 코드의 컴파일 결과가 운영체제별로 달라질 수 있었다. 하지만 Java는 컴파일 환경과 실행 환경을 분리하여 문제를 해결했다. 바이트 코드(*.class)라는 표준 중간 계층을 두어 Java 컴파일러가 소스 코드를 읽어들여 이 바이트 코드를 만든다.
Java 컴파일러는 실행환경 별로 표준화된 하나의 바이트 코드를 만들어낸다. 그리고 실행 환경별로 분리된 JRE가 바이트 코드를 읽어들여 해석하고 소스 코드를 실행한다. Java 실행환경(JRE)이 운영체제나 환경별로 구분되어 있는 방식이다.
즉 컴파일러는 환경별로 구분할 필요없이 표준화된 자바 바이트 코드를 만들기만 하면 된다. 모바일 장비에서 실행되던 자바로 개발된 앱을 가전 제품에서 동작하려면 가전 제품 전용 JRE만 개발하변 된다. 컴파일러를 다시 개발할 필요가 없다.
이러한 특징으로 WORA(Write Once, Run Anywhere)라는 슬로건으로 큰 인기를 얻기 시작했다. 뛰어난 이식성으로 다양한 플랫폼으로 개발할 수 있었다. 어떤 운영체제든 JRE만 구현하면 모든 자바 소스 코드를 동일하게 동작할 수 있다. (이러한 특징은 안드로이드가 자바를 택하는 결정적인 계기가 된다.)
자바 빌드 및 실행 과정을 정리하면 다음과 같다.
- 자바 소스 코드 파일(*.java)를 자바 컴파일러(javac)가 컴파일하여 표준화된 바이트 코드(*.class)를 만든다.
- 운영체제별로 구현된 자바 실행 환경(JRE)가 자바 가상 머신(JVM)을 통해 바이트 코드(*.class)를 운영체제에 맞게 해석하여 실행한다.
JRE 구조
운영체제별로 구현된 자바 실행 환경(JRE)가 자바 컴파일러가 컴파일한 결과인 *.class 파일을 실행한다. 이 JRE의 구조를 살펴보면 다음과 같다.
JRE는 각종 Java Class Library와 가상 실행 환경인 JVM으로 구성되어 있다. Java Class Library에는 우리가 흔히 알고있는 컬렉션 프레임워크, 참조 자료형, java.util.* 등이 포함된다. JVM은 자바의 컴파일된 바이트 코드를 각 운영체제에 맞게 실행해주는 핵심적인 역할을 수행한다.
Java의 빌드 실행 과정
간단하게 Java로 프로그램을 구현하고 빌드해서 실행하는 과정을 살펴보자. 먼저 다음과 같은 클래스 파일이 있다.
// Hello.java
public class Hello {
public static void main(String[] args){
System.out.println("Hello, World!");
}
}
위 소스 파일을 다음 명령어로 같이 빌드한다.
javac Hello.java
소스파일을 javac로 빌드하면 Hello.class라는 클래스 파일을 볼 수 있다. 이것이 자바 바이트 코드이다.
이렇게 생성된 Hello.class바이트 코드를 디스어셈블러를 이용해 살펴보면 다음과 같은 내용으로 구성되어 있다.
Hello.class 바이트 코드는 다음 명령어로 실행할 수 있다.
java Hello
JVM
JVM은 이렇게 표준화된 바이트 코드를 각각의 환경에서 실행할 수 있게 해준다. 예를 들어 윈도우용 JVM은 바이트 코드를 윈도우가 이해할 수 있는 기계어로 번역한다. 리눅스용 JVM은 바이트 코드를 리눅스가 이해할 수 있는 기계어로 번역해준다.
여기서 자바에 슬로건인 WORA(한 번 만들면 어디에서나 동작한다.)가 탄생한다.
JVM 기반 언어
Java가 인기를 끌고 JVM이 웬만한 운영체제를 모두 지원하기 시작하면서, 꼭 Java가 아니더라도, WORA의 특징을 누리길 희망하는 언어들이 등장하기 시작했다.
JVM 기반 언어는 Java Virtual Machine(JVM)에서 실행될 수 있도록 바이트코드(.class 파일)를 생성하는 언어를 뜻한다. JVM 기반 언어는 Java의 주요 특징인 WORA를 그대로 활용할 수 있다. 운영 체제와 환경 별로 소스코드를 해석하고 실행하는 과정은 JVM이 처리해주므로, 단순히 표준화된 바이트 코드만 만들어내면 되기 때문이다. 이때 Java와 호환성이 뛰어나면서 JVM 기반에서 동작할 수 있는 대표적인 언어인 Kotlin이 등장하게 된다.
Kotlin의 빌드와 실행
이번에는 Kotlin의 빌드 과정과 실행 방식을 알아보자. 앞서 살펴보았듯이, Java는 자바 가상 머신(JVM)이 각각의 환경별로 제공되기 때문에, 표준화된 바이트 코드만 제공하면 됐다. 환경별로 제공되는 JVM이 바이트 코드를 환경에 맞게 해석하고 실행하여 동작한다.
따라서 바이트 코드만 생성한다면, 꼭 자바가 아니여도 JVM으로 실행할 수 있다. 따라서 코틀린 컴파일러가 자바가 만든 것과 동일한 클래스 파일(바이트 코드)를 만든다면 사실상 성능은 동일한 것이다.
기존 자바의 빌드 및 실행 과정에서 코틀린의 경우를 추가하면 다음과 같다.
코틀린 컴파일러는 코틀린 소스 코드를 통해 표준화된 자바 바이트 코드를 만들어낸다. 이 자바 바이트 코드는 환경별로 제공된 JVM을 재사용할 수 있고 Java의 주요 특징인 WORA를 그대로 가져갈 수 있다.
출처
'Java & Kotlin' 카테고리의 다른 글
[Java] 자바의 두가지 정렬 방법(Comparable, Comparator) (0) | 2024.08.01 |
---|---|
[Java] 주요 함수형 인터페이스(Function Interface) (2/2) (0) | 2024.03.19 |
[Java] 함수형 프로그래밍(Functional Programming)과 람다식(Lambda Expression) (1/2) (0) | 2024.03.10 |
[Java] 변수 선언과 할당을 나눠야하는 경우와 그렇지 않은 경우 (0) | 2023.12.31 |
[Java] 메서드 및 필드 선언 순서 컨벤션(in 자바) (2) | 2023.12.31 |