가비지 컬렉션 (Garbage Collection, GC)

6971 단어 Weekly CSWeekly CS

❓JVM의 가비지 컬렉션이란?

JVM (Java Virtual Machine)이 운영체제의 메모리에 접근하여 메모리를 관리하는데 동적으로 할당한 메모리 영역 중 사용하지 않는 메모리 영역을 자동으로 해제시켜 관리해주는 것을 말한다.

👍🏻👎🏻가비지 컬렉션의 장단점

가비지 컬렉션은 C언어와 같이 개발자가 메모리를 해제하지 않고 JVM이 자동으로 메모리를 관리하기 때문에 “해제된 메모리 참조", “자동으로 메모리 해제” 등의 장점이 있다.

반대로, 가비지 컬렉션은 코드레벨에서 발생하는 것이 아니기 때문에 오버헤드가 발생하며, JVM이 가비지 컬렉션을 자동으로 진행하기 때문에 개발자는 어느 시점에 가비지 컬렉션이 실행되는지 모른다는 단점이 있다.

🗑️ JVM의 가비지 컬렉션

⚙️ JVM Runtime Area 구조

JVM의 가비지 컬렉션의 동작을 확인하기 전에 간단하게 JVM의 Runtime Area 구조에 대해 확인이 필요하다.

  1. Method Area : 클래스, 인터페이스, 메소드 등을 저장
  2. Heap Area : 런타임 시에 동적으로 생성되는 객체나 배열 인스턴스 등을 저장
  3. Stack Area : 로컬 변수, Heap Area에서 만들어진 객체들의 래퍼런스를 저장
  4. PC Register : 스레드가 생성되는 영역
  5. Native Method Stack : Java 이외의 언어로 작성한 코드를 저장

🛠️ Root Space(Method, Stack, Native Method Stack) 와 Heap Area에서의 동작

JVM의 가비지 컬렉션은 Method Area, Stack Area, Native Method Stack으로 이뤄진 Root Space에서부터 시작해 Heap Area에 도달한 객체들을 찾는다.

public Class Test {
	public static void main(String[] args) {
		int num = 1;           // Stack Area 저장
		String name = "Zayson" // name 객체변수는 Stack, Zayson이라는 객체값은 Heap에 저장
		List<String> list = new ArrayList<>(); // list 객체변수는 Stack, new로 생성된 객체는 Heap에 저장
	}
}

이렇게 Root Space 부터 Heap Area에 도달해 객체를 찾는 것을 “Mark” 한다고 한다.

그리고 도달하지 못해 어떤 Root Space에서도 도달되지 않고 참조되지 않는 객체들을 Unreachable Object라고 하고 이는 가비지 컬렉션의 대상이된다. 이를 “Sweep” 한다고 한다.

이렇게 남은 객체들을 정리하기도 하는데 이를 “Compaction(압축)” 한다고 하며, 이는 메모리 단편화를 방지해주는 역할을 하며 필수적으로 실행되지는 않는다.

⚒️ Heap Area 내에서의 동작

가비지 컬렉션은 JVM이 원할때 의도적으로 실행되어야 한다. 따라서, JVM은 Heap Area 내에서도 어떠한 기준을 통해서 가비지 컬렉션을 실행한다.

Heap Area를 조금 더 구체적으로 파헤쳐보면 “Eden”, “Survival 0”, “Survival 1”로 구성된 “Young Generation” 영역과 “Old Generation” 영역으로 구성된다.

Eden 영역은 새롭게 생성된 객체가 저장되는 영역으로 새롭게 객체가 생성되면 아래와 같이 메모리를 차지한다. 아래 객체의 숫자는 Age Bit를 나타낸다.

그렇게 Eden 영역이 가득 차게 된다면 JVM은 “Minor GC”를 진행한다.

Minor GC는 Eden 영역에서 사용하는 객체들을 마킹한 후, 비어있는 Survival 영역으로 이동시켜 준 후 Age Bit를 1 증가 시킨다.

Young Generation의 Survival 0과 Survival 1 영역 중 한 영역은 반드시 비어있다.

이후 , 다시 Eden 영역이 가득차게 되면 JVM은 다시 Minor GC를 진행한다. JVM은 Eden 영역과 Survival 0 영역에서 사용하는 참조하는 객체들을 마킹한 후, 비어있는 Survival 1 영역으로 이동시키고 Age Bit를 1 증가 시킨다.

이러한 동작이 반복되면서, Age Bit가 특정 기준에 도달하면, JVM은 해당 객체들을 오랫동안 참조해야할 객체로 인식한 후 Old Generation 영역으로 이동시킨다. 이렇게 이동시키는 것을 “Promotion” 한다고 한다.

다시 동작이 반복되면서, Old Generation 영역이 가득차게 되면 JVM은 Old Generation 영역에서 참조하는 객체들을 마킹한 후 Unreachable Object들을 Sweep 하는데 이를 “Major GC”라고 한다. Major GC는 Minor GC 보다 시간이 오래걸리며, 리소스도 많이 사용한다.

🗒️ JVM의 가비지 컬렉션 흐름 요약

  1. Root Space 부터 Heap Area까지 따라가서 도달한 객체(Reachable Object)는 Mark한다.
    1. 새로운 객체 Eden영역에 생성되고, Age bit가 0으로 들어간다.
    2. Eden 영역이 가득차게 되면, Minor GC가 발생하여 비어있는 Survival 영역으로 Reachable Object는 이동되고, Unreachable Object는 Sweep된다.
    3. b 단계를 반복하다가, 특정 Age Bit가 되면 Old Generation으로 이동되며 이를 Promotion이라 한다.
    4. Old Generation 영역이 가득차게 되면, Reachable Object를 Mark 하고, Unreachable Object를 Sweep 하며, Old Generation에서 이뤄지는 가비지 컬렉션을 Major GC라 한다.
  2. Unreachable Object는 Sweep 한다.

📎 가비지 컬렉터 종류

  1. Serial GC : 가비지 컬렉션을 처리하는 스레드가 1개이며, CPU 코어가 1개일 때 사용하는 방식이다.
  2. Parallel GC : 가비지 컬렉션을 처리하는 스레드가 여러개이며, Serial GC 보다 빠르다.
  3. Concurrent Mark Sweep GC ( CMS GC) : Stop-The-World 상태를 줄이기 위해 고안된 GC로 총 4단계를 거쳐 Reachable Object와 Unreachable Object를 파악한 후 GC를 실행한다 .
    1. Initial Mark : 현재 Root Space에서 참조하고 있는 객체를 마킹 (Stop-The-World)
    2. Concurrent Mark : 애플리케이션과 병행하면서 작동하며, 참조하고 있는 객체를 탐색하면서 새롭게 참조되거나 지속되는 객체를 마킹
    3. Remark : 지금까지 마킹한 객체들이 여전히 Root Space에서 참조되고 있는지 판단 (Stop-The-World)
    4. Concurrent Sweep : Unreachable Object를 Sweep
  4. G1 GC (Garbage First GC) : Heap 영역을 바둑판 형식의 Region 영역으로 쪼갠 후 가비지 컬렉션이 수행될 때 전체 Heap Area에 대해 수행하는 것이 아닌 Region 단위로 수행한다. Java 9버전 이상의 Default GC로 사용된다. 따라서, Stop-The-World가 줄어드는 장점이 있다.

Stop-The-World (STW) : 가비지 컬렉션을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것을 말한다.

📄 참조

조엘의 GC : https://youtu.be/FMUpVA0Vvjw

던의 JVM의 Garbage Collector : https://youtu.be/vZRmCbl871I

좋은 웹페이지 즐겨찾기