JVM 정복하기

Hyun·2023년 8월 10일
0

공부한거 정리

목록 보기
12/20

JVM 정복하기

  • 테코블 JVM에 관하여 시리즈를 기반으로, 보충하거나 정리하여 글을 작성하였다.

JDK, JRE, JVM


JDK

  • Java Development Kit

  • 자바를 활용하여 프로그램을 개발할 때 필요한 도구들의 모음

  • 구성요소

    • JRE

    • 컴파일러

      • xx.javaxx.class 로 컴파일
    • 디버거

  • 다양한 JDK 버전이 있다.

    • Oracle JDK

    • open JDK


JRE

  • Java Runtime Environment

  • JVM 위에서 프로그램이 원활하게 실행될 수 있는 환경 제공

    • .class 파일에 작성된 코드만으로는 부족하다.

    • 필수적인 라이브러리들이 필요하다.

  • 구성요소


JVM

  • Java Virtual Machine

  • 자바로 작성된 프로그램 (.class 파일) 이 돌아가도록 만들어주는 프로그램

  • 어느 기기, 운영체제와 상관없이 자바 프로그램은 JVM 위에서 실행 가능하다.

    • 높은 이식성

Class Loader

  • 클래스 파일 (.class) 들을 찾아 JVM의 메모리에 탑재한다.

  • Loading, Linking, Initialization 을 수행한다.


Loading

  • 필요한 클래스 파일들을 찾아 탑재한다.

    • 한번 JVM에 탑재되면 프로그램 종료전가지 JVM에서 제거되지 않는다.
  • 크게 3개의 계층으로 나뉜다.

    • Bootstrap ClassLoader

      • 모든 ClassLoader 의 부모

      • JVM을 구동시키기 위한 가장 필수적인 라이브러리들을 탑재

        • JDK/JRE/LIB 에 위치 (ex, rt.jar)
      • 운영체제에 맞게 네이티브 코드로 작성되어 있다.

    • Extensions ClassLoader

      • 자바의 표준 핵심 라이브러리들을 탑재
        • JDK/JRE/LIB/EXT 에 위치
    • Application ClassLoader

      • Classpath 에 있는 개발자들이 만든 클래스 파일들을 탑재
  • Delegation Model (위임 모델)

    • 클래스를 찾으라는 요청이 있을 때마다

    • ClassLoader Sub-SystemApplication ClassLoader 에게 위임하고

    • Application ClassLoaderExtensions ClassLoader 에게 위임하고

    • Extensions ClassLoaderBootstrap ClassLoader 에게 위임한다.

    • Bootstrap ClassLoader 이 요청받은 클래스를 찾지 못하면 Extensions ClassLoader 에게 제어권을 돌려주고

    • Extensions ClassLoader 이 요청받은 클래스를 찾지 못하면 Application ClassLoader 에게 제어권을 돌려주고

    • Application ClassLoader 이 요청받은 클래스를 찾지 못하면 ClassNotFoundException 예외 발생

  • Uniqueness Property

    • 탑재된 클래스들은 중복이 없다.

    • 항상 상향 위임을 시도하기 때문에, 상위 ClassLoader 가 클래스를 찾지 못한 경우에만

    • 현재 ClassLoader 가 클래스를 찾는다.

  • Visibility

    • 하위 ClassLoader 가 로딩한 클래스는

    • 상위 ClassLoader 가 로딩한 클래스를 볼 수 있다.

    • 반대는 불가능하다.

    • 이를 통해 클래스의 계층을 구분지어 관리할 수 있다.

  • ClassNotFoundException

    • 모든 ClassLoader 들을 거쳤는데도, 클래스 파일을 찾지 못하게되면 던지는 예외

프로그램 정상 수행


User.class 클래스 파일 제거

ClassNotFoundException 예외 발생


Linking

  • 로드된 클래스 파일들을 검증한다.

  • 클래스 파일들을 사용할 수 있게 준비한다.

  • 크게 3가지 단계로 이루어져 잇다.

    • Verification

      • 클래스 파일이 유효한지 검증한다.
    • Preparation

      • static field 메모리 할당 및 기본값으로 초기화
    • Resolution

      • Symbolic Reference ➔ Direct Reference 메모리 주소값으로 바꾼다.

Initialization

  • 클래스 파일의 코드를 읽는다.

  • 코드에서 지정한 값들로 필드들을 초기화 및 초기화 메서드를 실행한다.

  • 멀티 쓰레딩으로 동작하므로 동시성을 고려해야 한다.

  • static block 을 실행한다


Runtime Data Area

  • JVM 이 프로그램을 수행하기 위해 OS로부터 할당받는 메모리

  • 구성요소

    • Method Area (Metaspace)

    • Heap

    • Java Stacks

    • PC registers

    • Native Method Stacks


Method Area (Metaspace)

  • 클래스 구조, 메서드 데이터, 생성자 필드 데이터, 인터페이스, static 변수등을 저장

  • Runtime Constant Pool 이 할당된다.

    • 클래스마다 생성되는 상수 풀

    • 클래스가 사용하는 상수 값들을 저장

      • 숫자 리터럴 같은 컴파일타임 상수 + 메서드와 필드 참조같은 런타임 상수
  • JVM 당 하나만 생성

    • 모든 쓰레드가 Method Area 공유

    • 구동 시작시에 생성되고, 종료시까지 유지된다.

  • JVM의 다른 메모리 영역에서 해당 정보에 대한 요청이 오면, 해당 정보의 실제 물리 메모리 주소를 반환

  • Metaspace 는 Heap 공간이 아니다.

    • 자바 8 이후로 Permanent Generation 영역이 Metaspace 로 바뀌며 Heap에서 떨어져 나왔다.

Heap

  • 실제 데이터를 가진 인스턴스, 배열등을 저장

  • 문자열 정보를 가진 String Constant Pool 이 할당된다.

    • 참조를 잃은 String 리터럴은 GC의 대상이 된다.
  • JVM 당 하나만 생성된다.

    • Heap 영역의 데이터는 Java Stack 영역에서 참조되어 쓰레드간 공유된다.

    • Thread Safe 하지 않다.

  • Heap 이 가득차면 OutOfMemoryError 발생

  • Heap 에서 참조되지 않는 인스턴스와 배열은 GC의 주 대상이다.

  • JVM Native
    • 컴퓨터의 실제 물리적인 메모리 영역
    • JVM 자체와 JVM이 실행되는 환경에서 사용되는 네이티브 라이브러리, 운영체제, 기타 프로세스들과의 상호작용에 필요한 메모리 공간

Java Stack

  • 각 쓰레드별로 따로 할당된다.

    • Thread Safe 하다.
  • 메서드를 호출할 때마다 Frame 단위로 스택에 추가(push) 된다.

    • 메서드의 연산이 종료되면 결과값을 호출한 상위 Frame 에 반환하고, 스택에서 현재 Frame 을 제거(pop) 한다.
  • 각 Frame 에는 메서드의 지역 변수, 매개 변수, Operand Stack, Constant Pool Reference 로 이루어져 있다.

    • Operand Stack
      • 메서드 내 연산을 위한 피연산자 스택
    • Constant Pool Reference
      • Constant Pool 에 있는 상수를 가리키는 참조
  • Java Stack 영역이 가득차면 StackOverflowError 발생


Native Method Stacks

  • 다른 프로그래밍 언어로 작성된 메서드를 다루는 영역

    • 자바로만 처리할 수 없는 시스템 자원이나 API를 사용해야 하는 경우 다른 프로그래밍 언어로 작성된 Native Method 가 필요하다.

    • Java Stack 영역처럼 Native Method 가 실행될 경우, Stack 에 해당 메서드가 쌓인다.


PC (Program Counter) Registers

  • 명령어 주소값을 저장하는 공간

    • 각 쓰레드들은 자신만의 PC Register 를 갖는다.
  • 네이티브 메서드에서는 프로그램 카운터 값이 정의되지 않는다.


코드로 알아보는 Heap 과 Java Stack

슬라이드 : https://speakerdeck.com/deepu105/jvm-memory-usage-stack-vs-heap


Execution Engine

  • 메모리에 적재된 클래스 (바이트코드) 들을 기계어로 변경하여 명령어 단위로 실행

  • Interpreter 방식과 JIT 컴파일러 방식이 있다.


Interpreter

  • 런타임중에 바이트코드를 한 라인씩 읽으며 바로 실행한다.

  • C, C++ 처럼 미리 컴파일을 통해 기계어를 실행하는 언어에 비해 실행 속도가 느리다.


JIT 컴파일러

  • Just In Time 컴파일러 방식

  • 자주 실행되는 바이트코드 영역을 런타임중에 기계어로 컴파일하여 사용한다.

    • 컴파일 임계치를 만족하는 코드는 컴파일이 수행된다.

      • method entry counter (메소드 호출 횟수) + back-edge loop counter (메소드 내의 반복문 회전 횟수) = 컴파일 임계치

Garbage Collection

  • JVM 상에서 더 이상 사용되지 않는 데이터가 할당되어있는 메모리를 해제시켜준다.

  • Heap 영역 내의 참조되지 않는 데이터가 주 대상이다.


Reachability

  • Reachable : 참조되는 유효한 데이터

  • Unreachable : 참조되지 않는 데이터

    • GC의 대상이 된다.

  • Heap 영역 내부의 객체들을 reachable 로 만들어주는 Method Area, Java Stack, Native Stack 을 root set 이라 한다.
  • reachable 이 참조하는 다른 데이터도 reachable 이다.

Stop-The-World

  • GC 동작은 GC를 담당하는 쓰레드가 수행한다.

    • 다른 쓰레드들의 동작이 일시정지된다.
      • 성능 저하
  • 적절한 빈도의 GC 실행과, Stop-The-World 시간을 줄여 프로그램 성능을 높일 수 있다.


Weak Generational Hypothesis

  • 가비지 컬렉션때마다 모든 객체를 추적하는 것은 부하가 크다.

  • 대부분의 객체는 빠르게 unreachable 해진다고 가정

  • 오래된 영역에서 최신 영역으로의 참조 방향은 적다고 가정

  • 두 가정을 바탕으로 대부분의 object 들은 생성된 지 얼마 안 되어 소멸된다는 것을 관찰하였다.

  • 이를 최적화하기 위해 Heap 메모리를 세대 단위로 관리한다.

  • 특정 세대의 메모리 풀이 가득차면 해당 세대에서만 GC 가 실행된다.

GC 알고리즘

  • Mark And Sweep Algorithm

    • Mark Phase: root set에서 출발하여 참조되는 객체들을 마킹

    • Sweep Phase: 마킹되지 않은 객체들을 추적하며 삭제

    • 메모리가 Fragmentation (파편화) 된다는 단점이 있다.

  • Mark And Compact Algorithm

    • Mark And Sweep Algorithm 을 진행 후, 메모리를 정리하여 메모리 Fragmentation 을 해결

    • 주로 사용되는 알고리즘

  • 이러한 GC 알고리즘은 Major GC에서 사용한다.

Minor GC & Major GC

  • Minor GC

    • 주로 Young Generation 에서 일어난다.

      • Young Generation 안의 특정 영역이 가득 차 새로운 객체를 생성할 수 없을 때 발생

      • 해당 영역은 객체가 빨리 모여 금방 가득차기 때문에 성능이 좋은 Minor GC를 사용한다.

    • 마킹된 영역이 다음 영역으로 복사된다.

      • 삭제 과정은 이루어지지 않는다.

      • Major GC에 비해 짧은 시간 Stop-The-World 가 발생해, 빠르다.


  • Major GC

    • 주로 Old Generation 에서 일어난다.

    • 앞서 다룬 GC 알고리즘이 적용된다.

    • 긴 시간 Stop-The-World 가 발생해, 느리다.


출처

JVM에 관하여 - Part 1, JVM, JRE, JDK (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-07-12-jvm-jre-jdk/


JRE (Red Hat)
https://www.redhat.com/en/topics/cloud-native-apps/what-is-a-Java-runtime-environment


JDK, JRE 그림 (Java Guides)
https://www.javaguides.net/2019/02/java-jvm-jre-jdk-explained-with-diagrams.html


JVM에 관하여 - Part 2, ClassLoader (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-07-15-jvm-classloader/


Class Loader 이미지 (Java Tutorial Network)
https://javatutorial.net/tag/java-class-loader/


Class Loader Principle (Baeldung)
https://www.baeldung.com/java-classloaders


Class Loader in Java (GeeksforGeeks)
https://www.geeksforgeeks.org/classloader-in-java/


JVM에 관하여 - Part 3, Run-Time Data Area (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/


JVM Structure (Oracle)
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html


JVM Management (GeeksforGeeks)
https://www.geeksforgeeks.org/java-memory-management/


JVM에 관하여 - Part 3, Run-Time Data Area (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/


Heap & Stack 코드와 그림 (Deepu K Sasidharan 님)
https://dev.to/deepu105/visualizing-memory-management-in-jvm-java-kotlin-scala-groovy-clojure-19le


JVM Execution Engine (강준현 님)
https://junhyunny.github.io/information/java/jvm-execution-engine/


JVM 전체 그림 (DZone)
https://dzone.com/articles/jvm-architecture-explained


GC Weak Generational Hypothesis (브로리카 님)
https://brorica.tistory.com/entry/%EC%9E%90%EB%B0%94-gc


메모리 세대 그림 (Oracle)
https://docs.oracle.com/en/java/javase/16/gctuning/parallel-collector1.html#GUID-DCDD6E46-0406-41D1-AB49-FB96A50EB9CE


JVM에 관하여 - Part 4, Garbage Collection 기초 (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/

0개의 댓글