Post

Optimizing Java - JVM

1. 인터프리팅, 클래스로딩

1) 인터프리팅(Interpreting)

JVM은 스택 기반 인터프리터 머신이다. 중간 값을 스택에 담아두고 이 값으로 수행할 연산(Opcode)을 하나씩 처리하는 방식으로 간단하게 설명할 수 있다. JVM은 자바 파일(.java)을 바이트코드 파일(.class)로 컴파일 한 후 바이트코드 파일을 하나씩 인터프리팅하여 실행한다.

2) 클래스로딩(Class Loading)

바이트코드 파일을 하나씩 실행할 때 JVM은 모든 클래스를 메모리에 올려놓지 않는다. 모든 클래스를 JVM의 메서드 영역에 저장하는 것이 아닌 필요할 때마다 해당 클래스의 바이트코드 파일을 동적으로 로드하여 메모리에 올린다. 이 때 클래스 로더 또한 하나의 클래스로 다음과 같은 특징을 가진다.

  • 클래스 로더는 상속을 받을 수 있으며 필요한 경우 자식 클래스 로더는 부모 클래스 로더에게 작업을 위임한다.
  • 클래스 로더가 한번 로드한 클래스는 다시 메모리에서 제거할 수 없다.

(1) 부트스트랩 클래스 로더(BootStrap Class Loader)

부트스트랩 클래스 로더는 최초로 로드되는 클래스 로더로 다음 기능을 한다.

  1. 다른 클래스 로더를 로드한다.
  2. 자바의 기본 클래스(java.lang.Object, java.lang.String 등)를 필요할 때 로드한다.

(2) 확장 클래스 로더(Extension Class Loader)

부트스트랩 클래스 로더의 자식 클래스 로더로 특정한 OS나 플랫폼에 네이티브 코드를 제공하는 클래스 로더이다.

(3) 애플리케이션 클래스 로더(Application Class Loader)

확장 클래스 로더의 자식 클래스 로더로 지정된 클래스패스에 위치한 유저 클래스를 로드한다. 우리가 작성하는 클래스는 애플리케이션 클래스 로더가 바이트코드를 가져와 JVM의 메서드 영역에 저장한다.

(4) 클래스 로딩 과정

1
2
3
4
5
class HelloClass {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
  1. 애플리케이션 클래스 로더가 HelloClass를 찾아 JVM의 메서드 영역에 로드한다.
  2. HelloClass에서 참조되는 String과 System 기본 클래스를 부모 클래스인 확장 클래스 로더에 위임한다.
  3. 확장 클래스 로더는 부모 클래스인 부트스트랩 클래스 로더에게 String과 System 기본 클래스의 로드를 위임한다.
  4. 부트스트랩 클래스 로더는 String과 System 기본 클래스를 로드하여 JVM의 메서드 영역에 저장한다.
  5. 이후 참조되는 String과 System 기본 클래스는 재로딩과정 없이 JVM의 메서드 영역에서 클래스 정보를 바로 가져온다.

2. 바이트코드 실행

WORA(Write Once, Run Anywhere)을 표방하는 자바는 컴파일 과정과 인터프리터 과정 두 가지를 모두 사용한다. 사용자가 작성한 자바 파일을 JVM에 종속적인 바이트코드 파일로 변환하고 인터프리터 과정을 거치며 플랫폼에 종속되는 네이티브 코드로 변환된다. 사용자가 작성한 자바 파일은 JVM의 도움으로 JVM을 지원하는 모든 플랫폼에서 실행할 수 있게 된다.

1) 바이트코드 파일(.class) 구조

img_4.png

컴포넌트설명
Magic Number해당 파일이 .class 파일임을 나타낸다.(0xCAFEBABE)
클래스 파일 포맷 버전Magic Number 다음 4바이트는 클래스파일 포맷 버전을 의미한다.
(00 00 00 3D는 Major-Java 17, Minor-0을 의미)
상수 풀(Constant Pool)클래스의 상수(클래스명, 메서드명, 문자열 등)값을 저장하고 코드를 실행할 때 이 클래스 파일의 상수 풀에서 상수값을 가져온다.
액세스 플래그(Access Flag)클래스의 public/final, interface/abstract class, annotation/enum 인지 여부를 저장한다.
this, superclass, interface,
필드, 메서드, 속성
this(현재 클래스명), superclass(부모클래스명), 구현한 인터페이스, 정의된 필드, 메서드, 속성에 관한 정보가 정의되어 있다.

3. 핫스팟

img_5.png

오래전의 JVM은 컴파일 후 인터프리터하는 과정으로 매우 느렸으나 핫스팟 가상 머신이 등장한 후 많은 성능 개선이 이루어졌다. C와 C++과 달리 저수준의 제어를 언어에서 수행하는 자바는 JVM의 최적화를 통해 높은 생산성을 유지하면서 성능을 개선할 수 있었다. 핫스팟 JVM의 핵심 요소는 JIT 컴파일과 자동 메모리 관리이다.

1) JIT 컴파일

JIT 컴파일은 자주 실행되는 코드 파트는 실행될 때마다 인터프리트하는 것이 아닌 최초에 컴파일을 하여 리소스를 절약하는 방법이다. 컴파일러가 해석 단계에서 정보를 수집하여 특정 메서드가 자주 실행된다고 판단되면 JIT 컴파일을 적용한다.

갈수록 JIT 컴파일이 고도화되어 JIT 컴파일을 거친 후 실제 실행되는 코드는 처음 개발자가 작성한 코드와 다른 경우가 많다. 따라서 고성능의 자바 어플리케이션을 개발하는 경우 애플리케이션의 실제 동작 방식을 넘겨짚는 대신 앞서 언급했던 실험을 통한 측정으로 성능을 개선해야 한다.


Reference

https://brewagebear.github.io/fundamental-jvm-classloader/

This post is licensed under CC BY 4.0 by the author.

© . Some rights reserved.

Using the Chirpy theme for Jekyll.