본문 바로가기
Coding/Java

[Java] 자바 동작 과정

by Gofo 2024. 4. 1.

Java 동작과정

개요

  1. 자바 컴파일러가 소스코드를 바이트코드로 변환시킨다.
  2. 클래스로더가 바이트코드를 JVM의 runtime data area(메모리 영역)로 올리고
  3. Execution engine의 interpreter와 JIT compiler를 통해 코드를 해석한다.

 

 

컴파일? 인터프리터?

자바는 컴파일 방식과 인터프리터 방식 둘 다 사용한다.

  1. 컴파일 방식 : 자바 컴파일러는 소스코드를(*.java)를 자바 바이트코드(*.class)로 변환시키고,
  2. 인터프리터 방식 : JVM은 변환된 소스코드를 런타임에 읽어서 코드를 실행한다.

 


JVM 역할

Class Loader System

Java byte code (*.class)를 runtime에 읽어서 JVM의 메모리(runtime data area)에 배치시킨다.

 

Loading → Linking → Initialization의 단계를 가진다.

  1. Loading
    • 자바 바이트 코드(*.class)를 읽어서 적절한 binary data를 만들고 runtime data area의 method area에 저장한다.
    • static main() 메소드 부터 메소드 영역에 먼저 저장된다.
  2. Linking
    • 로드된 클래스나 인터페이스들을 검증(verify)하고 준비(prepare)하는 과정이다.
    • 로드된 클래스 데이터는 Verify(검증) → Prepare(준비) → Resolution(실행) 과정을 거친다.
      1. Verify : Java language specification (자바 언어 명세)와 JVM 명세에 명시된대로 구성되어있는지 검사한다.
      2. Prepare
        1. class가 필요한 메모리를 할당한다. (field, method, interface 등)
        2. static field는 기본값으로 생성되고 초기화된다.
      3. Resolve : 모든 symbolic reference들을 direct reference로 변경한다. 즉, 실제 객체의 주소를 참조하도록 변경한다.
  3. Initialization : static 변수들에 값을 할당하고 static 블럭을 실행한다.

 

Runtime Data Area

JVM이 프로그램을 실행하기 위해 OS로부터 할당받은 메모리 영역이다.

 

Stack + PC Registers + Native Method Stacks  + Heap + Method Area 로 구성되고,

Stack, PC Registers, Method Stacks는 각 스레드 별로 존재한다.

  • Stack
    • 각 스레드에서 코드를 실행하는데 사용하는 메모리 영역이다.
    • 메소드가 수행될 때 마다 하나의 스택 프레임이 생성(push)되고, 메소드가 종료되면 스택 프레임이 제거(pop) 된다.
      • 스택 프레임 : arguments, return address, local variable 등에 대한 정보를 담고 있다. (참고)
  • PC Registers
    • Program Counter Register
    • 현재 실행 중인 명령어의 위치(주소)를 가리킨다.
  • Native Method Stacks
    • Native method를 호출하는 코드를 수행하기 위한 스택 영역이다.
      • Native method : 자바가 아닌 언어에서 제공되는 메소드 (C, C++)
    • JVM stack과 native method stacks이 나뉘어져있지만, dynamic linking을 통해 확장될 뿐이다.

Stack - Native Method Stacks 연결 관계

  • Heap
    • new 키워드를 통해 생성된 객체를 위한 공간이다.
    • 참조하는 변수나 필드가 없다면 의미 없는 객체로 분류되어 garbage collector에 의해 제거된다.
  • Method Area
    • static 키워드가 붙은 데이터를 위한 공간이다.

 

Execution Engine

클래스 로더에 의해 runtime data area에 배치된 바이트 코드의 명령어를 읽어서 실행한다. 

 

Interpreter와 JIT Compiler를 이용해 명령어를 실행한다.

기존에는 인터프리터만 존재하였지만, 성능 향상을 위해 JIT 컴파일러가 추가되었다.

  • Interpretier
    • 명령어를 하나씩 읽어서 해석하고 실행한다. (기존 인터프리터 방식과 유사)
    • 하나하나의 실행은 빠르지만, 전체적인 실행 속도가 느리다.
  • JIT Compiler
    • JIT : Just In Time Compiler
    • 전체를 컴파일 하지 않고, 인터프리터가 자주 사용하는 메소스의 byte 코드를 컴파일하여 binary 코드로 변환한다.
    • 후에 해당 메소드를 사용할 때는 인터프리터로 해석하지 않고 저장된 binary 코드를 가져오게 된다.
    • 전체적인 실행 속도는 인터프리팅 방식보다 빠르다.
  • Garbage Collection
    • Heap을 대상으로 필요없는 객체(uncreachable)들을 제거하여 메모리 공간을 compact하게 만든다.
      • 객체가 null 인 경우
      • 블럭의 local 객체 (블럭 내부에서 생성된 객체)
      • 부모 객체가 null인 자식 객체

 


참고

 

 

 

 

댓글