JVM, GC(Garbage Collector)

전주현·2023년 10월 30일
0

개념 정리

목록 보기
1/5

- 공부하게 된 이유 -

static에 대한 이해가 부족했고 JVM 내부 동작에 대해 알게 되면 static 뿐만 아니라 메모리 최적화에도 도움을 얻을 수 있다는 조언을 얻어 공부하게 되었다.

1. JVM

JVM은 크게 세 가지 요소로 구성된다.

  • 클래스 로더(Class Loader)

    • 자바 컴파일러는 .java파일을 .class파일(바이트코드 형식)로 컴파일한다.
    • 클래스 로더는 이 컴파일된 클래스 파일을 로드하여 클래스 파일 정보를 메모리 영역에 올리고 검증하고 static(정적, 컴파일 이후 변하지 않는) 변수들을 초기화 하는 등의 역할을 한다.
  • 실행 엔진(Execution Engine)

    • 실행 엔진은 JVM이 로드한 클래스 파일을 실행한다. 실행 엔진에는 Interpreter, JIT Compiler, Garbage Collector가 있다.
    • 이 중 Interpreter와 JIT Compiler는 바이트 코드를 기계어로 변환하여 os에 전달한다.
    • Interpreter 방식은 바이트 코드를 한 줄씩 해석하고 실행하고 JIT COmpiler 방식은 인터프리터가 반복해서 변환하는 코드를 동적으로 기계어로 컴파일하여 실행 속도를 향상 시킨다.
  • 메모리 영역(Memory Areas)

    • 자바 프로그램의 실행과 관련된 데이터와 자원을 저장한다. 주요한 메모리 영역은 다음과 같다.
      • 메서드 영역(Method Area) : 클래스 정보, 상수 풀(Constant Pool), 메서드 코드 등의 정보를 저장한다.
      • 힙(Heap) : 동적으로 생성된 객체와 배열을 저장한다. 가바지 컬렉터가 불 필요한 객체를 지우는 곳
      • 스택(Stack) : 메서드 호출과 관련된 데이터를 저장하는 곳, 쓰레드마다 별도의 스택이 생성, 메서드 호출 시 프레임이라는 자료구조로 각 메서드마다 하나씩 쌓임
      • PC Register : 현재 실행되고 있는 명령어의 주소를 저장하고 있는 곳, 멀티 쓰레드 환경에서 한 쓰레드가 작업을 하다가 멈추고 다른 쓰레드에게 cpu 점유를 넘겨주고 다시 돌아왔을 때 이전에 어떤 명령을 수행하고 있었는지 기억하고 있는 곳
      • Native Method Stack) : Java 외부에서 사용되는 네이티브 코드(C/C++)로 작성된 메서드를 실행할 때 사용되는 스택

2. Java Runtime Data Area

위 JVM의 메모리 영역이 Java Runtime Data Area이다. 이 영역은 크게 두가지로 나뉜다.

  • 모든 쓰레드가 공유하는 영역
    • Method Area
    • Heap
  • 쓰레드 종료 시 소멸되는 스택 영역
    • Stack(JVM Language Stacks)
    • PC Registers
    • Native Method Stacks

3. GC(Garbage Collector)

동적으로 할당한 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능

  • Stack
    • 정적으로 할당한 메모리 영역
    • 원시 타입의 데이터가 값과 함께 할당
    • Heap영역에 생성된 Object 타입의 데이터의 참조 값 할당
  • Heap
    • 동적으로 할당한 메모리 영역
    • 모든 Object 타입의 데이터가 할당
    • Heap 영역의 Object를 가리키는 참조 변수가 Stack에 할당

GC가 필요한 이유

  • 장점
    • 메모리 누수 낮출 수 있음
    • 해제된 메모리에 재접근하는 낭비 줄임
    • 해제한 메모리 다시 해제하는 낭비 줄임
  • 단점
    • GC 작업은 순수 오버헤드, 프로그램이 해야 하는일처럼 컴퓨터 리소스를 써서 GC를 하는 동안 프로그램이 멈춰야 하기 때문
    • 개발자는 언제 GC가 메모리를 해제하는지 정확히 알기 어려움

GC 대표 알고리즘 2가지

- Reference Counting

  • Reference count는 몇가지 방법으로 해당 객체에 접근할 수 있는지를 뜻함
  • 해당 객체에 접근할 수 있는 방법이 하나도 없다면, 즉 reference count가 0이 되면 GC의 대상이 됨
  • reference couting 알고리즘에은 순환 참조 문제가 있음, 오른쪽 그림처럼 꼬리를 물고 서로가 서로를 참조하고 있으면 무조건 1이 되어 GC의 대상이 되지 않아 Memory Leak이 발생함
  • 루트 스페이스 : 스택 변수, 전역 변수 등 Heap 영역 참조를 담은 변수

- Mark And Sweep

  • JVM GC가 기본적으로 돌아가는 방식

  • Reference Counting의 순환 참조 문제를 해결 가능

  • 루트에서부터 객체까지 접근 가능한지가 기준

  • Mark : 그래프 순회를 통해 연결된 객체를 찾아내는 것

  • Sweep : 연결이 끊어진 객체를 지우는 것

  • 루트로부터 연결된 객체는 Reachable, 끊어진 객체는 Unreachalbe

  • 이렇게 Sweep 이후에 분산된 메모리를 정리해서 메모리 파편화를 막는 것은 Compaction

  • Mark and Sweep에서 Compaction은 필수는 아님

  • Mark and Sweep의 단점

    • 의도적으로 GC를 실행시켜야 한다.
    • 어플리케이션 실행과 GC 실행이 병행된다.

4. GC와 Heap

  • JVM의 Heap 영역은 Young GeneraionOld Generation으로 나뉜다.

  • Minor GC : Young Generation에서 발생하는 GC
  • Major GC : Old Generation에서 발생하는 GC
  • Eden은 새롭게 생성된 객체들이 할당되는 영역
  • Survival은 Eden이나 다른 Survival이 꽉찼을 때 Minor GC로부터 살아남음 Reachalbe한 객체가 존재하는 영역
  • Survival의 영역의 둘 중 한곳은 반드시 비어있어야 함!
  • Minor GC로부터 살아남으면 객체의 age-bit가 증가함
  • Promotion : age-bit가 일정 수준 넘게 되면 Old Generation으로 넘어가는 것 ( Java 8에서 Parallel GC 방식 사용기준, age-bit가 15가 되면 Promotion 진행 )
  • Old Generation도 꽉차게 되면 Major GC가 실행 되고 Major GC는 Minor GC보다 오래 걸림
  • 굳이 Young, Old로 나눈 이유는 GC 설계자들이 어플리케이션을 분석해보니 대부분의 객체가 수명이 짧다는 것을 발견, 특정 부분만 탐색하며 해제하기 위해 Young 사용

5. GC 방식

  • Stop The World
    GC를 실행하기 위해 JVM이 어플리케이션 실행을 멈추는 것, 이 stop the world의 시간을 최적화 하는것이 GC의 최적화
  1. Serial GC
  • 하나의 쓰레드로 GC를 실행
  • Stop The World 시간이 긺
  • 싱글 쓰레드 환경 및 Heap이 매우 작을 때 사용
  1. Parallel GC
  • 여러 개의 쓰레드로 GC를 실행
  • 멀티코어 환경에서 사용
  • Java 8의 default GC 방식
  1. CMS(Concurrent Mark-Sweep) GC
  • Stop The World 최소화를 위해 고안
  • GC 작업을 어플리케이션과 동시에 실행
  • G1 GC 등장에 따라 대체되서 안쓰임
  1. G1(Garbage First) GC
  • Heap을 Region으로 나누어 사용
  • Java 9 이후 부터 default GC 방식
  • 런타임에 G1 GC가 필요에 따라 영역별 Region 개수를 튜닝함

*** GC 튜닝(개념만 알고 가고 나중에 해보자)

  • 성능 개선 단계의 최종단계, 객체 생성자체를 줄이려는 코드 레벨에서의 개선이 선행된 후 진행하는 것
  • Old Generation으로 넘어가는 객체 최소화하기
  • Major GC 시간을 짧게 유지하기

참고한 사이트

profile
개발

0개의 댓글

관련 채용 정보