Technology/Java

Java 프로그램의 실행원리(feat. JVM)

ikjo 2021. 10. 17. 00:39

자바 프로그램의 실행원리?

자바를 통해 웹 개발을 하던 문득 "자바로 프로그래밍한 프로그램은 어떻게 실행되는 것일까?"라는 의문이 들었다. 우선 자바 프로그램이 동작하는 단계는 총 2단계로 컴파일 단계실행 단계로 나눌 수 있었는데 이를 다루기 먼저 자바 가상 머신(JVM, Java Virtual Machine)이라는 것을 다룰 필요성이 있다. 이는 아래 글을 참고해볼 수 있다.

 

 

JVM이란 무엇인가? JVM 파헤쳐보기

JVM이란 무엇인가? JVM(Java Virtual Machine)은 앞서 언급했듯이 자바 가상 머신으로서 자바를 실행하기 위한 가상 기계(컴퓨터)라고 할 수 있다. 이때 가상 기계란 소프트웨어로 구현된 하드웨어로서

ikjo.tistory.com

 

 

컴파일 단계

자바 소스 코드 파일

우선 컴파일 단계부터 살펴보자. 자바 개발자들이 이클립스, 인텔리제이 등 개발 환경(IDE)에서 작성하는 자바 소스 코드 파일(*.java) 자체를 "자바 파일" 이라고 한다. 이는 사람(개발자)만이 이해할 수 있는 코드로 당연히 사용자 컴퓨터의 운영체제는 네이티브 코드가 아닌 이 코드를 이해할 수 없다.

 

자바 소스 코드 파일(*.java) 예시

 

자바 소스 컴파일러와 클래스 파일

개발자가 개발 환경에서 이러한 자바 소스 코드 파일을 작성하고 저장하는 순간 JDK(자바 개발 도구) 안에 포함된 자바 소스 컴파일러(javac.exe)라는 프로그램이 해당 자바 파일을 컴파일(Compile) 하면서 클래스 파일(*.class)로 변환시키는데 이 클래스 파일을 자바 애플리케이션이라고 한다. CLI 환경에서는 javac 명령어를 통해 자바 소스 코드를 별도로 컴파일할 수도 있다. 참고로 이러한 클래스 파일을 '자바 목적 파일'이라고도 한다.

 

앞서 JDK를 잠시 언급했는데, JDK는 JVM(자바 가상 기계)용 소프트웨어 개발 도구로서 JVM을 포함하고 있는 JRE(자바 실행환경) 뿐만 아니라 자바 소스 컴파일러, 디버거 등 자바 프로그램 개발을 위한 여러 도구들을 포함하고 있다.

 

JDK(자바 개발 도구) JVM용 소프트웨어 개발 도구 JRE, 자바 소스 컴파일러(javac.exe), 각종 개발 도구(jar 등) 등
JRE(자바 실행 환경) JVM용 OS  JVM, 자바 프로그램 실행기(java.exe), 라이브러리 등
JVM(자바 가상 기계) 가상 컴퓨터 클래스 로더, 메모리 영역, 실행 엔진

※ 참고 : C언어에서 컴파일 작업은 "컴파일러 프로그램"이 "전처리기 프로그램"에 의해 전처리된 C 소스 코드를 '어셈블리어' 라는 저수준의 프로그래밍 언어로 변환하는 작업을 말한다.

 

image src: https://www.geeksforgeeks.org/differences-jdk-jre-jvm/

 

이러한 클래스 파일은 개발자가 이해할 수 있는 자바 소스 코드와 달리 바이트 코드로 되어 사람이 이해하기 거의(?) 어려운 수준이지만 마찬가지로 사용자 컴퓨터의 운영체제에게도 아직 이해하기 어려운 수준이다. 참고로 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1 바이트이기 때문에 바이트 코드라고 불린다.

 

클래스 파일(*.class) 예시

 

앞서 사용자 컴퓨터의 운영체제가 클래스 파일을 이해하기 어렵다고 했지만 JVM은 클래스 파일을 이해할 수 있어 일련의 과정들을 통해 이를 해석하여 사용자 컴퓨터의 운영체제별로 이해할 수 있는 네이티브 코드로 변환시켜준다.

 

 

실행 단계

이제부터 실행 단계로서 자바 소스 컴파일러에 의해 변환된 클래스 파일을 실행해야한다. 이때 자바 소스 코드의 main 메서드자바 프로그램이 실행되는 시작점이자 실행이 종료되는 끝점이 된다.

 

자바 실행기(java.exe)로 클래스 파일을 실행하는 순간 JRE는 해당 클래스 파일 내 main 메서드가 있는지 등 프로그램 실행을 위한 사전 준비를 완료 후 이상이 없는 경우 JVM을 구동(부팅)시키는데, 이제부터는 JVM이 일하게 된다.

 

전처리

부팅된 JVM은 가장 먼저 전처리라는 과정을 수행한다. 이 과정에서는 JVM을 이루는 구성 요소 중 "클래스 로더"라는 것이 동작하는데, 클래스 로더는 "로딩", "링크", "초기화" 이 세 과정을 통해 java.lang 패키지에 속한 클래스와 개발자가 작성한 클래스 그리고 임포트(import)된 패키지에 속한 클래스들을 동적으로 JVM 메모리 영역 中 메서드 영역에 적재한다.

 

main 메서드의 실행 및 종료

(쓰레드에 관한 내용은 생략했다.) 이후 main 메서드가 실행되기 위해 스택 프레임이 JVM 메모리 영역 中 스택 영역에 할당된다. 정확히는 main 메서드 정의를 시작하는 여는 중괄호 "{"를 만날 때 스택 프레임이 생긴다. 이후 스택 프레임 내에는 main 메서드와 관련된 지역 변수나 메서드 매개변수 공간이 할당된다.

 

참고로 스택 프레임이란 스택 영역에 차례대로 저장되는 메서드의 호출 정보(지역 변수, 매개변수, 호출이 끝난 뒤 돌아갈 반환 주소값 등)를 의미한다. 이러한 스택 프레임 덕에 메서드의 호출이 모두 끝난 뒤에, 해당 메서드가 호출되기 이전의 상태로 되돌아 갈 수 있는 것이다.

 

이후 main 메서드가 실행되기 시작하며, main 메서드 내 작성된 코드들이 실행된다. main 메서드에서 또 다른 메서드를 호출 시 앞선 방식과 동일하게 해당 메서드 정의를 시작하는 여는 중괄호 "{"를 만날 때 해당 메서드에 대한 새로운 스택 프레임이 스택 영역에 생기게 된다. 또한 중간에 객체나 배열을 생성하는 작업을 한다면 JVM 메모리 영역 中 힙 영역에 해당 정보를 저장한다.

 

모든 메서드가 호출이 완료된 이후 main 메서드의 끝을 나타내는 닫는 중괄호 "}"를 만나면 main 메서드의 스택 프레임이 소멸됨과 함께 모든 스택 프레임이 제거되면서 JRE가 JVM을 종료하고 JRE 자체도 운영체제 상의 메모리에서 사라지게 된다.

 

바이트 코드를 네이티브 코드로 변환하는 실행 엔진

앞서 전처리 시 클래스 정보들이 JVM 메모리 영역 中 메서드 영역에 적재되고, main 메서드 실행 시 여러가지 스택 프레임이 JVM 메모리 영역 中 스택 영역에 생기고, 여러 객체나 배열이 JVM 메모리 영역 中 힙 영역에 생기는 것을 알 수 있었다.  이렇게 JVM 메모리 영역에 쌓인 데이터(바이트 코드)들은 JVM을 이루는 실행 엔진에 의해 처리되어 네이티브 코드로 변환되어 운영체제가 이를 처리한다.

 

 

최종 정리

지금까지 크게 컴파일 단계실행 단계를 거쳐 개발자가 작성한 자바 소스 코드가 바이트 코드로, 그리고 바이트 코드가 네이티브 코드로 변환되는 과정을 간략하게 알아보았다.

 

이러한 일련의 과정을 통해 비로소 개발자가 작성한 소스 코드가 사용자 컴퓨터의 운영체제가 이해할 수 있는 언어(네이티브 코드)로 번역된 것이다. 이 네이티브 코드는 다시 사용자 컴퓨터의 운영체제별로 해석되고 운영체제의 명령에 따라 컴퓨터 하드웨어에 의해 실제 프로그램이 구현될 것이다.

 

image src: https://www.nowwatersblog.com/cs/JVM

 

결론적으로 컴파일/실행 과정은 자바 소스 컴파일러와 JVM이 알아서 해주기 때문에 개발자 입장에서는 자바 소스 코드를 한번만 작성해도 어느 OS에서든 이를 실행 시킬 수 있는 것이다. 즉, Write Once Run Anywhere(WORA)가 가능해진 것이다.

 

실행 흐름 요약

컴파일 단계(개발자 영역)

자바 소스 코드(*.java) → 자바 소스 컴파일러(javac.exe) 코드 변환 → 바이트 코드(*.class)

 

실행 단계(사용자 영역)

바이트 코드 → 자바 프로그램 실행기(java.exe)의 JVM 부팅 → JVM(클래스 로더 → 메모리 → 실행 엔진) → 네이티브 코드

 

 

참고 자료

  • 위키북스 "스프링 입문을 위한 자바 객체 지향의 원리와 이해"
  • 도우출판  "자바의 정석"
  • http://www.tcpschool.com/c/c_memory_stackframe