Bibbidi Bobbidi Boo
article thumbnail
Published 2023. 6. 22. 00:06
[TIL/개념] Garbage Collection Java∕Kotlin

* TIL/개념: 최대한 공식 문서 & 책을 기반으로 배운 내용을 정리

* 현재 취준생으로 풋내기 개발자가 쓰는 글입니다.

* 그러니 조언과 지적 및 훈수는 언제나 환영입니다! 댓글로 많이 달아주세요!

 

이전 포스팅 ⟪JVM과 메모리 구조⟫에서

JVM은 GC를 이용해서 메모리 관리를 한다고 배웠다.

해당 글에 이어서 이번 포스팅에서는 Garbage Collector, 줄여서 GC에 대해 알아보자!



Garbage Collector는 어떻게 메모리를 수집하는가?


Garbage Collection이란?

Garbage collection is the process of looking at heap memory, identifying which objects are in use and which are not, and deleting the unused objects.

가비지 컬렉션은 힙 메모리에서 사용하지 않는 공간을 삭제하는 프로세스를 말한다.

여기서 사용하지 않는 공간은 어느 곳에서도 참조하고 있지 않은 공간이다.


Garbage Collection의 장단점

C, C++와 같은 언어에서 직접 malloc(), 혹은 new 을 호출하고, free()를 호출해서 수동으로 관리를 했어야 했으나, 

GC를 이용해 해당 작업의 부담을 덜게 되었다.

 

그러나 성능 측면에서 단점이 존재하는데,

바로 GC 스레드는 작업을 수행하기 위해서 다른 모든 스레드를 중단한다는 것이다.

이는 결국 애플리케이션이 중단되는 것과 마찬가지.

이 때문인지 GC가 발생할 때 "Stop-The-World"가 발생했다고도 말한다.

 

이런 단점을 극복하기 위해서는 중단하는 그 시간이 적어도 혀용 가능한 범위가 되도록,

GC를 조정하고, JVM 최적화를 수행할 필요가 있다.


GC의 대상이 되는 객체

실제로 사용되지 않으므로 GC의 대상이 되거나, 될 수 있는 객체의 예시는 다음과 같다:

 

1. Nullifying the reference variable

객체를 가리키는 참조 변수에 null을 할당하며 연결을 끊는 방법이다.

val object = MyObject() // 객체 생성
// 객체를 사용한 후
obj = null // obj에 null 값을 할당하여 객체와의 연결을 끊음

 

2. Re-assigning the reference variable

참조 변수에 새로운 객체를 할당하여 이전 객체와의 연결을 끊는 방법이다.

var obj = MyObject() // 첫번째 객체 생성
// obj를 사용한 후
obj = MyObject() // obj에 새로운 객체를 할당하여 이전 객체와의 연결 끊음

 

3. An object created inside the method

메서드 내에서 생성된 객체는

메서드의 실행이 완료되고 나면 더 이상 접근할 수 없어 사용할 수 없게 된다.

사용할 수 없는 해당 객체는 GC의 대상이 된다.

fun createObj() {
    val obj = MyObject() // 메서드 내에서 객체 생성
    // obj 사용한 후
    // 메서드 실행 종료 후 obj는 접근할 수 없음
}

 

4. Island of Isolation

객체 1은 객체 2를 참조하고, 객체 2는 객체 1을 참조하고 있다.

다른 객체는 객체 1과 2를 참조하고 있지 않을 때 이를 lsland of lsolation(고립의 섬) 이라고 부른다.

class MyObject {
    var i: MyObject? = null
}

fun main() {
    var t1: MyObject? = MyObject()
    var t2: MyObject? = MyObject()
	
    // 1) 서로가 서로를 참조
    t1?.i = t2 // t1이 t2를 참조하도록 설정
    t2?.i = t1 // t2가 t2을 참조하도록 설정
	
    // 2) t1을 null로 설정: t2와 t2.i로 사용 가능
    t1 = null
    
    // 3) t2를 null로 설정: 두 객체 모두 사용할 수 없음 → GC 수집 대상
    t2 = null
}

위 코드을 실행했을 때 힙에서 일어나고 있는 일은?

 

1) 맨 처음 t1과 t2가 서로를 참조

t1, t2로 두 객체에 접근해 사용할 수 있는 상태다.

 

2)  t1 = null

t1은 t2.i로,

t2는 t2를 통해 도달할 수 있다. 이 때는 GC 수집 대상이 아니다.

3) t2 = null

t1, t2 모두 객체에 도달할 수 있는 방법이 없다.

t1, t2 모두 호출할 수 있는 방법이 없으므로 이 때 GC 대상이 된다.

 


GC를 실행하기 위해 JVM에 요청하는 방법

코드로 Garbage Collector를 실행하도록 요청도 할 수 있다.

// System 클래스 사용
System.gc()
// Runtime 클래스 사용
Runtime.getRuntime().gc()

두 방법 모두 실질적으로는 동일하며,

GC를 실행한다는 보장은 없다.


Mark And Sweep

가비지 콜렉터를 수행하기 위해서는 

  1. 힙 내의 모든 객체를 알 수 있어야 한다.
  2. 실질적으로 쓰지 않는 힙 공간을 회수하고 다시 사용할 수 있어야 한다.

이를 위해서 Mark And Sweep 알고리즘을 사용한다.

해당 알고리즘은 이름 그대로 Mark 단계, Sweep 단계 순서로 수행된다.

 

1단계: Mark 단계

모든 변수를 스캔하면서 어떤 객체를 가리키고 있는지 찾는 과정, 즉 Stop-the-World가 발생하는 단계

객체가 생성될 때 Mark Bit는 0으로 설정된다.

Mark 단계에서 Root로부터 그래프 순회를 통해 도달 가능한 객체, 즉 사용하고 있는 객체는 1로 설정한다.

이 때, DFS를 사용한다.

fun mark(root: Int) {
    if (markedBit[root] == 0) {
        // 사용 중인 객체 mark
        markedBit[root] = 1
        // 연결된 객체 방문
        for (v in graph[root]) {
            mark(v)
        }
    }
}

 

2단계: Sweep 단계

이름 그대로 도달할 수 없는 객체를 소거한다. 즉, 힙 내에서 연결되지 않은 메모리는 지운다.

0으로 Mark된 객체, 즉 사용하고 있지 않는 객체는 힙 메모리에서 지우도록 설정한다.

fun sweep() {
    for (p in heap) {
    	// 사용 중이면
        if (markedBit[p] == 1) {
            markedBit[p] = 0
        } else { // 사용 중이 아니면
            heap.release(p) // 할당 해제
        }
    }
}

요약

  • Garbage Collection: 힙 메모리에서 사용하지 않는 공간을 삭제하는 프로세스
  • GC의 장단점:
    • 장점: 수동으로 메모리 할당/해제를 할 필요가 없어 개발자의 부담이 덜함
    • 단점: GC 스레드가 작업을 수행하기 위해서 다른 모든 스레드를 중단(Stop-The-World)
  • GC의 대상이 되는 객체
    1. 참조 변수에 null 할당
    2. 참조 변수에 새로운 객체 할당
    3. 종료된 메서드 내에서 생성된 객체
    4. Island of Isolation(고립의 섬)
  • GC 알고리즘: Mark And Sweep
    1. Mark 단계: 모든 변수를 스캔하면서 어떤 객체를 가리키고 있는지 찾는 과정
      • 이 때 Stop-The-World가 발생
      • DFS 사용
    2. Sweep 단계: 도달할 수 없는 객체를 소거

 


참고 자료


마치며

후기

'Java∕Kotlin' 카테고리의 다른 글

[TIL/개념] JVM과 메모리 구조  (0) 2023.06.17
profile

Bibbidi Bobbidi Boo

@비비디

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!