반응형
Garbage Collection Guide

들어가기 전에

이번 프로젝트를 진행하면서 많이 보던 에러 중 하나가 Heap Size Error였다.
Salesforce의 수많은 제약 중 하나가 메모리 부족인데, 메모리 부족 현상을 해결할 수 있는 방법을 찾아보면 다음과 같은 방법들이 있다.

  1. RAM을 사라
  2. Garbage Collection을 튜닝해라

여기서 Garbage Collector, 즉 GC에 대해 알아본 기억이 없었다.
GC의 개념에 대해서 간단히 알아보고, 사용하는 알고리즘에 대해 공부해보려고 한다.

Garbage Collection이란 무엇인가?

Garbage Collection: 더 이상 사용하지 않는 메모리를 정리하는 기능

대부분의 프로그래밍 언어에서 함수(또는 메서드)가 실행되면 지역 변수가 생성되거나 새로운 값이 할당된다.
이때 변수 자체는 스택(Stack) 같은 실행 컨텍스트에 저장되며, 값이 참조형(예: 객체, 리스트, 맵 등)인 경우에는
별도의 힙(Heap) 메모리 영역에 실제 데이터가 저장된다.

함수 실행이 종료되더라도, 힙에 저장된 데이터는 다른 곳에서 참조되고 있다면 메모리에 계속 남아 있게 된다.
하지만 이렇게 사용되지 않는 데이터들이 힙에 쌓이게 되면 메모리 공간이 부족해지고, 심할 경우
OutOfMemoryErrorHeap Size Error 같은 메모리 부족 오류가 발생할 수 있다.

이러한 상황을 방지하기 위해, Garbage Collector(GC)는 프로그램 실행 중
더 이상 참조되지 않는 객체를 자동으로 감지하고 메모리에서 제거해준다.

Garbage Collection이 삭제하는 기준

GC는 HEAP 영역에서 더 이상 참조되지 않는 객체들을 자동으로 삭제하여 메모리 공간을 회수하는 역할을 수행한다.
그렇다면, GC는 어떻게 더 이상 사용하지 않는다.고 판단할까?

Mark & Sweep

GC는 삭제 대상을 탐색하기 위해 GC Root를 기준으로 객체를 추적한다.

GC Root란 현재 프로그램 실행 중 직접 참조되고 있는 객체를 의미하며, GC는 이 GC Root에서 시작해 객체 간의 참조 관계를 따라가며
사용 중인 객체를 찾아 마킹(Mark)하고, 마킹되지 않은 객체는 참조되지 않는 것으로 간주하고 삭제(Sweep)하여 메모리를 회수한다.

이를 순서대로 정리하면 다음과 같다:

  1. GC Trigger 동작

    특정 조건에 의해 GC가 동작한다.

  2. GC Root 수집

    현재 실행 중인 로직에서 직접 참조되고 있는 객체(GC Root)를 수집한다.

  3. GC Root로부터 참조 그래프 탐색 및 마킹

    수집한 GC Root를 시작점으로, 객체 간 참조를 따라가며 도달 가능한 객체들을 Mark한다.

  4. Mark되지 않은 객체 삭제

    참조되지 않고 Mark되지 않은 객체들은 더 이상 사용되지 않는 것으로 판단하고, Heap 메모리에서 삭제한다.

흐름

GC 실행 → GC Root 수집 → 참조 탐색(Mark) → 쓰이지 않는 객체 삭제(Sweep)

장점

  • 메모리 전체를 한 번에 처리 가능
  • 순환 참조 처리 가능: GC Root에서 순환 참조 관계인 객체를 실행시키지 않으면, Mark되지 않으므로 해당 객체에 할당된 메모리가 삭제됨

단점

  • 메모리 단편화 발생: 삭제 후 빈 공간이 흩어져 메모리 효율이 낮아짐
  • 시간이 오래 걸림
  • Stop The World 발생 가능

Mark & Compact

Mark & Sweep에서 마킹된 객체를 메모리 한쪽으로 이동시켜 압축하여 단편화를 해결한다.

  1. GC Trigger 동작

    메모리 부족 등 조건에 의해 GC 시작

  2. GC Root 수집

    직접 참조되는 GC Root 객체 수집

  3. GC Root로부터 참조 그래프 탐색 및 마킹

    GC Root에서 시작해 도달 가능한 객체를 Mark

  4. 객체 삭제 후 압축

    Mark되지 않은 객체 삭제하고, 나머지 객체들을 메모리 시작점으로 이동시킴.

흐름

GC 실행 → GC Root 수집 → 참조 탐색 → Mark되지 않은 객체 삭제 후 남은 객체 압축

특징

장점

  • 메모리 단편화 문제 해결
  • 새로운 객체 할당에 용이

단점

  • 압축 과정에서 추가적인 시간 필요
  • 참조 주소 업데이트 필요

Mark & Copy

메모리를 두 공간으로 나누고, 도달 가능한 객체만 To 공간으로 복사하고 From 공간을 비워 메모리를 회수한다.

  1. GC Trigger 동작: 메모리 부족 등으로 GC 시작.
  2. GC Root 수집: GC Root 객체 수집.
  3. GC Root로부터 참조 그래프 탐색 및 복사: GC Root에서 도달 가능한 객체를 탐색하며 To 공간으로 복사.
  4. 표기되지 않은 데이터 삭제: From 공간의 복사되지 않은 객체는 삭제하고, From 공간 전체를 비움(초기화).

흐름

GC 실행 → GC Root 수집 → 참조 탐색 및 Mark된 객체 복사 → 원본 데이터 영역 초기화

특징

장점

  • 따로 삭제하지 않고, 해당 영역을 덮어쓰거나 초기화하여 시간 단축
  • 메모리 단편화 발생하지 않음 (살아있는 객체만 복사)

단점

  • 사용할 수 있는 메모리 공간이 줄어듦
  • 살아있는 객체가 많으면 비용이 커짐

Reference Counting

객체 참조 횟수를 추적하고 참조 수가 0이면 즉시 삭제한다. 흔히 Stack 메모리에서 메서드가 작동할 때, 객체 참조가 발생하면 +1,
메서드가 종료되면 -1 되는 방식이다.

  1. GC Trigger 동작

    참조 횟수 변경(객체 참조/해제) 시 즉시 또는 주기적으로 실행.

  2. 참조 횟수 추적

    객체 생성 시 참조 횟수 초기화, 참조/해제 시 증감.

  3. 참조 횟수 확인

    참조 횟수가 0인 객체 식별

  4. 참조 횟수 0인 객체 삭제

    참조 횟수가 0인 객체를 즉시 메모리에서 제거

흐름

GC 실행 → 로직 실행 시 참조 증감 → 참조 횟수가 0인 객체 해제

특징

장점

  • 즉각적으로 메모리 회수하여 시간 감소
  • 구현하기 쉬움

단점

  • 순환 참조 처리 어려움
  • 참조 횟수 관리로 인한 추가 오버헤드
  • 잦은 참조 변경 시 성능 저하 가능

글을 마무리 하며

가비지 컬렉터와 가비지 컬렉터에서 사용하는 삭제 알고리즘에 대해서 간략하게 알아보았다.
다음 포스팅에서는 현재 사용하고 있는 가비지 컬렉터의 알고리즘에 대해서 알아보고 정리하려고 한다.

반응형

'Java' 카테고리의 다른 글

Garbage Collector : Generation GC, G1 GC, ZGC  (0) 2025.05.15
Java - 자바는 왜 쓸까? 장단점  (1) 2024.11.27
Java - 간단한 소개와 객체지향  (2) 2024.11.05

BELATED ARTICLES

more