본문 바로가기

Java

JVM 가비지 컬렉터(GC) Garbage Collector

728x90

JVM (Java Virtual Machine)

어떠한 운영체제나 CPU 아키텍처 환경에서도 자바 바이트 코드를 실행 할 수 있도록 만들어진 자바 가상 머신이다.

자바 컴파일 후 나오는 class 파일이 자바 바이트 코드를 담고있다.

 

JVM 구조

자바 코드 수행 과정

JVM은 크게 4가지로 구성되어있다.

  • Class Loader : 컴파일러(javac)에 의해 만들어진 자바 바이트 코드를 Runtime Data Areas로 적재를 수행한다.
  • Execution Engine : Runtime Data Areas에 적재된 자바 바이트 코드를 기계어로 변경하고 이를 실행한다. 하나읽고 하나 실행하는 인터프리터 방식과 이를 보완한 JIT(Just-In-Time) 방식이 있다.
  • Runtime Data Areas : 운영체제로부터 메모리를 할당받고 자바 어플리케이션을 실행하는 영역이다.
  • Garbage Collector : Runtime Data Areas의 메모리 영역중 Heap영역에 사용되지 않는 객체들(Garbage)을 제거 한다.

간략히 정리하면 클래스 로더(Class Loader)가 컴파일된 자바 바이트코드를 런타임 데이터 영역(Runtime Data Areas)에 로드하고, 실행 엔진(Execution Engine)이 자바 바이트코드를 실행한다.

실행 하고 Heap에 남아있는 쓸모없는 객체를 가비지 컬렉터가 정리한다.

 

RunTime Data Areas

런타임 영역은 모든 쓰레드가 공유하는 메모리 영역과 쓰레드 별로 생성되는 메모리 영역으로 나뉜다.

 

모든 쓰레드가 공유하는 메모리 영역

  • Method Area : 클래스와 인터페이스에 대한 정보, static, final, 모든 메서드 정보 등 모든 쓰레드가 공유하고 메모리에 항상 상주하는 영역
  • Heap Area : 동적으로 할당한 메모리 영역. 모든 Object 타입의 데이터가 할당되고 할당된 객체를 가리키는 참조 변수는 Stack Area에 할당된다. 가비지 컬렉터에 의해 관리된다.

 

쓰레드 별로 생성되는 메모리 영역

  • Stack Area : 쓰레드별로 스택 프레임 1개씩 생성되는 메모리 영역. 원시 타입의 데이터를 값과 함께 할당하고, Heap 영역에 생성된 객체 데이터의 참조 값을 할당한다.
  • PC Registers : 쓰레드가 생성될 때 마다 생기는 공간으로, 쓰레드가 실행하는 명령에 대해 기록되는 영역
  • Native Method Stack : 자바 외의 언어로 작성된 네이티브 코드를 위한 영역

 

 

Stack과 Heap 예

public class Main {
	public static void main(String[] args) {
    	int num1 = 1;
        int num2 = 5;
        int sum = num1 + num2;
        String name = "홍길동";
        
        System.out.println(name);
    }
}

이 소스코드를 돌린다고 가정하면

Stack에는 num1=1, num2=5, sum=6, name이 할당된다.

Heap에는 String | 홍길동 이 할당되고 Stack의 name이 이를 참조한다.

 

메인 메서드가 끝나면 Stack에 할당된 데이터가 전부 Pop 되고

Heap에 남은 String 홍길동은 남게되어 Unreachable Object가 된다. => 가비지

 

 

GC 과정

Heap 영역의 가비지 (Unreachable Object)를 제거하는 과정이다.

 

1. 가비지 컬렉터가 Stack의 모든 변수를 스캔하면서 참조된 객체들(Reachable Object) 즉 살아있는 객체들을 마킹한다.

2. 마킹되지 않은 객체를 Heap에서 제거한다.

 

1번 과정을 Mark, 2번 과정을 Sweep이라고 부르며 GC를 Mark and Sweep 과정이라고 부른다.

 

* GC 방식에 따라 Compact 과정이 추가로 존재하는경우가 있는데, 이는 Sweep후 분산된 데이터를 Heap의 시작주소로 모으는 과정을 말한다.

 

 

Heap영역

Heap 메모리 영역은 그림과 같이 Young Generation과 Old Generation으로 구분되며,

Young Gen에서 발생하는 GC를 Minor GC

Old Gen에서 발생하는 GC를 Major GC라고 정의한다.

 

Young Gen은 Eden영역과 Survivor0, 1영역으로 나누어지는데 메모리가 다 차게될때 Minor GC가 발생하고 남은 Reachable Object들이 옮겨지게 된다.

 

Young Generation의 Minor GC

1. 새로운 객체가 할당되면서 Eden에 메모리가 다차게 되면 Minor GC가 발생한다.

2. Eden에서 Mionor GC 발생 후 남은 객체들이 Survivor0, 1영역중 객체가 차있는 영역으로 옮겨진다.

3. Eden에서 새로운 객체가 할당되면서 메모리가 또 차게 되면 Minor GC가 발생하고, 살아남은 객체들은 Survivor 0, 1 영역중 객체가 차있는 영역으로 옮겨진다.

4. Survivor 0, 1 영역 중 객체가 있던 영역이 꽉차게 되면 Minor GC가 발생하며 살아남은 객체들은 비어있는 Survivor 영역으로 옮겨진다.

 

위 과정이 계속 반복되는데 Young Generation에 특정 임계점 만큼 오래 남은 데이터는 Old Generation으로 옮겨진다.

Major GC는 이렇게 Old Generation이 꽉차게 된 경우에 발생한다.

 

GC의 종류

  • Serial GC
  • Parallel GC
  • Concurrent Mark Sweep GC
  • G1 GC (Garbage First)

 

Serial GC

GC를 처리하는 쓰레드가 싱글 쓰레드인 방식으로 *stop-the-world가 길다는 특성을 가지고 있다.

Compact 과정이 추가된 알고리즘을 사용한다.

* stop-the-world

- GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것으로, GC를 실행하는 쓰레드 외에 모든 쓰레드가 작업을 중단한다. GC 완료 후 중단된 작업을 다시 시작한다.

 

 

Parallel GC

Young Gen의 GC를 멀티 쓰레드로 작업하는 방식으로 Java 8의 디폴트 GC이다. 멀티 쓰레드이기 때문에 Serial GC에 비해 stop-the-world의 시간이 감소된다.

Concurrent Mark Sweep GC (CMS GC)

Parallel GC처럼 멀티 쓰레드를 사용하는 방식으로 Stop-the-world 시간을 최소화 하기위해 4가지 과정으로 나누어 작업하는 방법. Compact 과정이 없다.

 

Initial Mark - GC Root에서 참조하는 객체(힙 외부에서 접근할 수 있는 객체)들만 우선 식별 STW(stop-the-world)발생

Concurrent Mark - 이전 단계에서 식별한 객체들이 참조하는 모든 객체 추적

Remark - 이전 Concurrent Mark 단계에서 식별한 결과를 다시 검증. 새로 생긴 객체나 참조가 끊긴 객체를 추적

Concurrent Sweep - 다른 애플리케이션 작업과 같이 진행하면서 최종적으로 Unreachable 객체 삭제

 

 

 

728x90