Kotlin 컴파일 과정과 JVM의 구조

강승구·2023년 2월 27일

코틀린과 JVM

코틀린은 JetBrain에서 개발한 JVM 기반 프로그래밍 언어이다. 그렇기 때문에 자바와의 상호운용성을 제공한다.

안드로이드와 코틀린을 공부하며 코틀린이 어떻게 컴파일 되는지 궁금하였고 이와 더불어 JVM의 구조에 대해 공부하고 정리해보았다.

자바와 코틀린은 OS에 독립적인 특성을 가지고 있다. 이게 가능한 이유는 JVM 때문이다.
JVM이란 Java Virtual Machine의 줄임말로 직역하면 자바를 실행하기 위한 가상 기계라고 할 수 있다.

JVM은 자바와 운영체제 사이의 중개자 역할을 하고 자바가 운영체제에 구애 받지 않고 프로그램을 실행할 수 있도록 도와주는 역할을 한다.
즉 JVM이란, OS에 종속받지 않고 CPU가 자바를 인식 & 실행할 수 있게 하는 가상 컴퓨터이다.

코틀린의 컴파일 과정

  1. 개발자가 소스 코드를 작성한다. (.kt 파일)
  2. 코틀린 컴파일러는 소스 코드를 바이트 코드로 컴파일한다. 이는 개발자가 작성한 코드를 JVM이 읽을 수 있는 코드로 변환하는 과정을 말한다.
    코틀린은 자바와 상호 운용할 수 있도록 개발되었기 때문에 생성된 바이트 코드는 자바에서 생성된 바이트 코드와 동일하게 동작한다.
  3. 컴파일된 바이트 코드는 .class 파일로 저장되고 이 파일은 클래스 로더로 전달된다.

JVM의 구조

JVM은 아래와 같이 구성되어 있다.

  • 클래스 로더
  • 실행 엔진
    • 인터프리터
    • JIT 컴파일러
    • Garbage Collector(GC)
  • 런타임 데이터 영역 (Runtime Data Area)
    • Method Area
    • Heap Area
    • Stack Area
    • PC Register
    • Native Method Area

1. 클래스 로더

클래스 로더는 JVM 내로 .class 파일(바이트 코드)을 동적으로 로드하고, 링크를 통해 배치하는 작업을 수행한다.
클래스를 메모리에 올리는 로딩 기능은 한번에 메모리에 올리지 않고 어플리케이션에서 필요한 경우 동적으로 메모리에 적재한다.

2. 실행 엔진

실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행하는 역할을 수행한다.

바이트 코드는 JVM이 이해할 수 있는 중간 레벨(기계어와 소스 코드의 중간 레벨)로 컴파일된 코드이다. 따라서 실행 엔진은 바이트 코드를 기계가 이해할 수 있는 기계어로 번역하는 역할을 담당한다.

이 과정에서 실행 엔진은 인터프리터와 JIT 컴파일러 두 가지 방식을 혼합하여 바이트 코드를 실행한다.

인터프리터

바이트 코드 명령어를 한줄씩 읽어서 해석하고 바로 실행한다.
JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작한다. 하지만 같은 메소드가 여러번 호출되는 경우에도 매번 같은 코드를 해석하고 수행해야하기 때문에 비효율적이라는 단점이 있다.

JIT 컴파일러

인터프리터의 단점을 보완하기 위해 JVM의 실행엔진은 JIT 컴파일러를 도입하였다. JIT 컴파일러는 반복되는 코드를 찾아 해당 코드 전체를 컴파일하여 Native Code로 변경하고 이후에는 해당 코드를 더이상 인터프리트 방식으로 실행하지 않고 캐싱해 두었다가 Native Code로 직접 실행한다.

Native Code란 Java에서 부모가 되는 C언어나 C++, 어셈블리어로 구성된 코드를 의미한다.

하지만 바이트 코드를 Native Code로 변환하는 과정은 비용이 소모되는 작업이기 때문에 JVM은 모든 코드를 JIT 컴파일러로 실행하는 것이 아닌 인터프리터를 사용하다 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행하는 방식을 사용한다.

Garbage Collector (GC)

JVM은 GC를 이용해 Heap 영역에서 더이상 사용하지 않는 메모리를 자동으로 회수해준다.
C언어 같은 경우, 개발자가 직접 메모리를 해제해주어야하지만 Java는 GC를 이용해 자동으로 메모리를 실시간으로 최적화 시켜준다.

3. Runtime Data Area

런타임 데이터 영역은 JVM의 메모리 영역으로 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역으로 Method Area, Heap Area, Stack Area, PC Register, Native Method Area로 나눌 수 있다.

이때 Method와 Heap 영역은 모든 스레드가 공유하는 영역이고 나머지 영역들은 각 스레드마다 고유한 개별 공간을 갖는다.

Method Area

profile
강승구

0개의 댓글