본문 바로가기

프로그래밍/Java

JVM (Java Virtual Machine)

자바를 사용해 애플리케이션을 만들고, 실행하는데 가장 중요한 것은 어떤것일까?

깔끔한 코드.

잘 만들어진 프레임워크.

자바가 작동되는 환경.

...

뛰어난 개발자가 작성한 최고의 코드도 환경이 제대로 구성 되어있지 않으면 무용지물이다. 애플리케이션이 작동하는 토대가 되는 실행 환경이 가장 중요하게 여겨지는 것도 어떻게 보면 당연한 일이다.

자바의 개발 환경.

그 안에 있는 자바의 실행 환경.

그 안에는 JVM과 필수 라이브러리가 있어 자바 애플리케이션이 작동하게 된다.


JVM

자바의 실행 환경.

JRE(Java Runtime Environment).

JRE는 JVM과 실행에 필수적인 라이브러리 등으로 구성되어있다.

JVM은 컴파일러가 컴파일한 클래스(.class) 파일을 로드하고 실행한다.

 

 

JVM은 어떻게 작동하는가?

크게 세가지 구성으로 구분할 수 있다.

  • Class Loader
  • Runtime Data Area
  • Execution Engine

 

1. Class Loader

클래스 로더는 컴파일 시점이 아닌 런타임 시점에 Load, Link, Initialize 한다.

1.1 Loading

이 컴포넌트에서 클래스가 로드될 때 도움을 주는 세가지 클래스 로더가 있다

  • Boot Strap Class Loader
    • 3가지 기본 클래스 로더 중 최상위 클래스 로더.
    • rt.jar 파일만 존재한다.
  • Extension Class Loader
    • jre/lib 폴더에 있는 클래스를 로드한다.
  • Application Class Loader
    • 애플리케이션 Classpath에 있는 클래스를 로드한다.
    • 개발자가 작성한 대부분의 클래스는 이 애플리케이션 클래스 로더에서 로드한다.

1.2 Linking

  • Verify
    • 생성된 바이트코드가 적절한지 검증.
  • Prepare
    • 모든 정적 변수에 메모리 할당.
    • 기본값이 지정됨
  • Resolve
    • Symbolic Reference를 메모리 상에서 이름에 맞는 객체의 주소를 찾아서 연결.
  •  

1.3 Initialization

모든 정적 변수의 값이 할당됨.

정적 블록 (static block) 이 실행됨.

 

2. Runtime Data Area

Runtime Data Area은 5개의 영역으로 나뉜다.

2.1 Method Area

정적 변수를 포함한 모든 클래스 수준의 데이터가 저장된다.

JVM 당 하나의 메소드 영역만 있으며 공유되는 자원이다.

2.2 Heap Area

모든 오브젝트와 객체 정보를 저장한다.

JVM 당 하나의 힙 영역만 있다. 각기 다른 스레드에서 접근 가능하다.

2.3 Stack Area

모든 쓰레드에 대해 별도의 런타임 스택이 생성된다.

모든 메소드 호출에 대해 스택 프레임이라고 하는 스택 메모리 항목이 만들어진다.

모든 지역 변수는 스택 메모리에 생성된다.

스택 영역은 쓰레드 세이프하다.

스택 프레임은 세 가지 하위 항목으로 구분된다.

 

  • Local Variable Array
    • 메소드와 관련된 지역 변수와 해당 값이 이곳에 저장된다.
  • Operand Stack
    • 중간 작업이 필요한 연산을 수행하는 경우 Operand Stack은 해당 연산의 작업 영역으로 작동한다.
  • Frame Data
    • Stack Frame을 구성하고 있는 영역.
    • 메소드가 정상 종료, 비정사 종료했을 때 발생하는 정보를 저장한다.

2.4 PC Registers

각 쓰레드는 별도의 PC Register를 가진다.

명령어가 실행 되면 현재 실행중인 명령어의 주소를 기억한다.

2.5 Native Method Stacks

네이티브 메소드 정보를 저장.

각 쓰레드 마다 별도의 네이티브 메소드 스택이 생성.

 

3. Execution Engine

Runtime Data Area에 할당된 바이트 코드를 컴퓨터가 이해할 수 있는 기계어로 변경해 명령어 단위로 실행한다.

3.1 Interpreter

바이트 코드를 한 줄 씩 해석한다. 같은 메소드가 여러번 호출 될 경우 매 번 해석해야하기 때문에 비효율적이다.

3.2 JIT Compiler

인터프리터의 단점을 보완한다.

인터프리터 방식으로 작동하다 반복되는 코드를 찾게 되면 전체 바이트 코드를 컴파일하고 네이티브 코드로 변경한다.

3.3 Garbage Collector

참조되지 않는 힙 영역의 객체를 수집, 제거한다.

 

Java Native Interface

Native Method Libraries와 상호 작용하고 Execute Engine에 필요한 Native Libraries를 제공한다.

Native Method Libraries

Execute Engine에 필요한 Native Libraries의 모음.

References

https://www.linkedin.com/pulse/jvm-architecture-detail-suresh-g

https://homoefficio.github.io/2018/10/13/Java-클래스로더-훑어보기/