[Java] gc에서 사용하지 않는 객체를 어떻게 구분하는가

2026. 3. 18. 00:39·Java/Java Concept

개요

오늘은 Java의 핵심 개념 중 하나인 Garbage Collector(GC)가 사용하지 않는 객체를 어떤 기준으로 구분하는지 정리해보려고 합니다.

그동안은 GC를 단순히 "더 이상 필요 없는 객체를 자동으로 제거해 주는 기능" 정도로만 이해하고 있었지만, 한 스타트업의 기술면접을 계기로 객체의 생존 여부를 판단하는 방식과 Reachability 개념을 좀 더 깊이 있게 살펴보게 되었습니다.

이번 글에서는 GC가 불필요한 객체를 어떻게 구분하는지와 자바의 참조 타입을 기반으로 동작 과정을 하나씩 정리해 보겠습니다. 정리 내용은 내용이 너무 유익해서 해당 링크를 전적으로 참고하였습니다. 

 

 

GC Reachability

GC(Garbage Collection)에서 사용하지 않는 객체를 구분하는 기준은 핵심적으로 도달 가능성(Reachability) 입니다.

GC의 일반적인 과정

  1. 힙(heap) 내의 객체 중에서 가비지(garbage)를 찾아냅니다.
  2. 찾아낸 가비지를 처리해서 힙의 메모리를 회수합니다.

GC 사용자 코드 관여 시점

  • JDK 1.2 이전: 가비지 컬렉션(Garbage Collection, 이하 GC) 작업에 애플리케이션의 사용자 코드가 관여하지 않도록 구현되어 있었습니다.
  • JDK 1.2 이후: JDK 1.2부터는 java.lang.ref 패키지를 추가해 사용자 코드와 GC가 상호작용할 수 있게 하고 있습니다.

java.lang.ref 패키지는 일반적인 객체 참조인 strong reference 외에도 soft, weak, phantom 3가지의 새로운 참조 방식을 각각의 Reference 클래스로 제공합니다.

 

Java GC는 객체가 가비지인지 판별하기 위해서 reachability(도달 가능성)라는 개념을 사용하게 됩니다. 어떤 객체에 유효한 참조가 있으면 'reachable'로, 없으면 'unreachable'로 구별하고, unreachable 객체를 가비지로 간주해 GC를 수행합니다.

 

Java GC는 Reachability Analysis를 사용해 객체의 생존 여부를 판단합니다. GC Root Set(참조의 시작점)를 시작점으로 객체 그래프를 탐색하여 도달 가능한 객체는 reachable로 유지하고, 도달할 수 없는 객체는 unreachable로 판단해 가비지로 수거합니다. 

 

이제 Runtime Area에서 객체 참조를 할 수 있는 4가지 영역에 대해서 설명드리겠습니다. 

 

JVM Runtime Data Area

 

JVM Runtime Data Area

힙 메모리에 존재하는 객체 참조는 4가지로 분류됩니다.

  • 힙 내의 다른 객체에 의한 참조
  • Java 스택, 즉 Java 메서드 실행 시에 사용하는 지역 변수와 파라미터들에 의한 참조
  • 네이티브 스택, 즉 JNI(Java Native Interface)에 의해 생성된 객체에 대한 참조
  • 메서드 영역의 정적 변수에 의한 참조

힙 내의 다른 객체에 의한 참조를 제외한 나머지 3개가 root set으로, reachability를 판가름하는 기준이 됩니다. 즉, GC는 Root Set을 시작점으로 객체 그래프를 탐색하여, 도달 가능한 객체는 유지하고 도달할 수 없는 객체를 수거 대상으로 판단합니다.


여기서 한 가지 중요한 점은, 객체가 단순히 참조되고 있는지 여부뿐만 아니라 "어떤 타입의 참조로 연결되어 있는지"에 따라서도 수거 여부가 달라진다는 것입니다. 이때 등장하는 개념이 바로 Java의 Reference Type입니다.

 

Java Reference Type

  • 자바의 참조타입은 총 4가지로 분류됩니다.
    • Strong References
    • Weak References
    • Soft References
    • Phantom References

Strong References 

 

root set으로부터 시작한 참조 사슬에 속한 객체들은 reachable 객체이고, 이 참조 사슬과 무관한 객체들이 unreachable 객체로 GC 대상입니다. 오른쪽 아래 객체처럼 reachable 객체를 참조하더라도, 다른 reachable 객체가 이 객체를 참조하지 않는다면 이 객체는 unreachable 객체입니다.

 

또한 강한 참조가 활성화된 객체는 가비지 수집 대상이 아닙니다. 강하게 참조된 변수가 null을 가리킬 때만 객체가 수집됩니다. 

 

Example

Strong References

MyClass obj = new MyClass (); // 가비지 컬렉터 수집 X (강한 참조)
obj = null; // 강한 참조 변수가 null을 가리키기 때문에 가비지 컬렉터 수집 O

// Java program to illustrate Strong reference
class Geeks
{
    //Code..
}
public class Example
{
    public static void main(String[] args)
    {
         //Strong Reference - by default
        Geeks g = new Geeks();    
        
        //Now, object to which 'g' was pointing earlier is 
        //eligible for garbage collection.
        g = null; 
    }
}

 

new 키워드로 생성된 객체는 강한 참조를 나타내기에 가비지 컬렉션의 대상이 될 수 없지만, 해당 객체가 null을 가리키는 순간 가비지 컬렉션의 대상이 될 수 있습니다.

 

Weak References

  • 약한 참조 객체는 참조 객체의 기본 유형/클래스가 아니므로 사용 시 명시적으로 지정해야 합니다.
  • 대상 객체를 참조하는 경우가 WeakReference 객체만 존재하는 경우 GC의 대상이 됩니다.
    • 해당 케이스에서 GC 실행 시 무조건 힙 메모리에서 제거됩니다.

Example

Weak References

//Java Code to illustrate Weak reference
import java.lang.ref.WeakReference;
class Gfg
{
    //code
    public void x()
    {
        System.out.println("GeeksforGeeks");
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        // Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        // Creating Weak Reference to Gfg-type object to which 'g' 
        // is also pointing.
        WeakReference<Gfg> weakref = new WeakReference<Gfg>(g);
        
        //Now, Gfg-type object to which 'g' was pointing earlier
        //is available for garbage collection.
        //But, it will be garbage collected only when JVM needs memory.
        g = null; 
        
        // You can retrieve back the object which
        // has been weakly referenced.
        // It successfully calls the method.
        g = weakref.get(); 
        
        g.x();
    }
}

 

강한 참조를 제거하고 약한 참조만 남게 되면, 해당 객체는 더 이상 GC Root와 강하게 연결되어 있지 않으므로 가비지 컬렉션의 대상이 될 수 있는 상태(weakly reachable)가 됩니다. 다만, 실제로 언제 수거될지는 GC 실행 시점에 따라 달라집니다.

 

Soft References

  • Soft References에서는 객체가 가비지 컬렉션 대상이 되어도 JVM에 메모리가 절실히 필요할 때까지는 가비지 컬렉션 되지 않습니다.
  • JVM의 메모리가 부족해지면 해당 객체가 메모리에서 제거됩니다.
  • 이러한 참조를 만들기 위해 SoftReference가 사용됩니다.

Example

Soft References

 

//Code to illustrate Soft reference
import java.lang.ref.SoftReference;
class Gfg
{
    public void x()
    {
        System.out.println("GeeksforGeeks");
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        // Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        // Creating Soft Reference to Gfg-type object to which 'g' 
        // is also pointing.
        SoftReference<Gfg> softref = new SoftReference<Gfg>(g);
        
        // Now, Gfg-type object to which 'g' was pointing
        // earlier is available for garbage collection.
        g = null; 
        
        // You can retrieve back the object which
        // has been weakly referenced.
        // It successfully calls the method.
        g = softref.get(); 
        
        g.x();
    }
}

 

Soft Reference 역시 강한 참조가 제거되면 해당 객체는 GC의 대상이 될 수 있는 상태가 됩니다.

하지만, Weak Reference와 달리, Soft Reference는 메모리가 부족한 상황에서만 GC에 의해 객체가 수거됩니다. 

 

즉, 현재와 같이 g = null로 강한 참조를 제거한 이후에도, JVM의 메모리가 충분하다면 해당 객체는 유지되면 softref.get() 메서드를 통해 다시 해당 객체에 접근할 수 있습니다.

 

따라서 Soft Reference는 캐시와 같이 가능하면 유지하고, 메모리가 부족할 때만 제거해도 되는 데이터를 관리할 때 주로 사용됩니다.

 

soft reference vs weak reference 

  • Soft Reference는 Strong Reference가 제거되면 GC 대상이 될 수 있다는 점에서는 Weak Reference와 동일합니다.
  • 하지만 Weak Reference는 GC가 수행되면 바로 수거되는 반면, Soft Reference는 메모리가 부족해질 때까지 최대한 유지된다는 차이가 있습니다.

Phantom References

  • Phantom References로 참조되는 객체는 가비지 컬렉션 대상이 될 수 있습니다.
  • JVM은 메모리에서 해당 객체를 제거하기 전에 'ReferenceQueue'라는 곳에 보관합니다.
  • 해당 객체에 대해 finalize() 메서드(GC 수행 이전에 리소스 정리를 위한 메서드)를 호출한 후에 생성됩니다.
//Code to illustrate Phantom reference
import java.lang.ref.*;

class Gfg
{
    //code
    public void x()
    {
        System.out.println("GeeksforGeeks");
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        //Strong Reference
        Gfg g = new Gfg();     
        g.x();
        
        //Creating reference queue
        ReferenceQueue<Gfg> refQueue = new ReferenceQueue<Gfg>();

        //Creating Phantom Reference to Gfg-type object to which 'g' 
        //is also pointing.
        PhantomReference<Gfg> phantomRef = null;
        
        phantomRef = new PhantomReference<Gfg>(g,refQueue);
        
        //Now, Gfg-type object to which 'g' was pointing
        //earlier is available for garbage collection.
        //But, this object is kept in 'refQueue' before 
        //removing it from the memory.
        g = null; 
        
        //It always returns null. 
        g = phantomRef.get(); 
        
        //It shows NullPointerException.
        g.x();
    }
}

 

이번 정리를 통해 GC는 단순히 "안 쓰는 객체를 제거한다"는 개념이 아니라, Root Set을 기준으로 Reachability를 분석하고, Reference Type에 따라 객체의 생명주기를 다르게 관리하는 메커니즘이라는 점을 이해할 수 있었습니다.

 

<참고 자료>

https://d2.naver.com/helloworld/329631

https://www.geeksforgeeks.org/java/types-references-java/

 

Types of References in Java - GeeksforGeeks

Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.

www.geeksforgeeks.org

 

 

저작자표시 비영리 변경금지 (새창열림)

'Java > Java Concept' 카테고리의 다른 글

[Java] Collection.foreach(), Stream.foreach()를 비교해보자  (0) 2025.09.01
[Java] Clean Code가 무엇이고 왜 사용해야 하는가  (2) 2024.07.24
[Java] inner class를 static으로 선언해야 하는 이유  (0) 2024.05.16
[Java] enum 비교에서는 equals(), == 둘 중 어떤 것이 적합한가?  (0) 2024.05.16
[Java] HashMap, HashTable, ConcurrentHashMap 자료구조에 대해서 알아보자  (2) 2024.01.22
'Java/Java Concept' 카테고리의 다른 글
  • [Java] Collection.foreach(), Stream.foreach()를 비교해보자
  • [Java] Clean Code가 무엇이고 왜 사용해야 하는가
  • [Java] inner class를 static으로 선언해야 하는 이유
  • [Java] enum 비교에서는 equals(), == 둘 중 어떤 것이 적합한가?
SeungbeomKim
SeungbeomKim
[IT(PS, CS, SW, etc.) 지식 기록] Github : https://github.com/daily1313/
  • SeungbeomKim
    개발 블로그
    SeungbeomKim
  • 전체
    오늘
    어제
    • 분류 전체보기 (409) N
      • 일상 (35)
        • 여행 (17)
        • 회고록 (10)
        • 리뷰 (8)
      • PS (138)
        • 그리디 알고리즘[Greedy] (25)
        • 정렬 알고리즘[Sort] (18)
        • 문자열 알고리즘[String] (14)
        • 동적 계획 알고리즘[DP] (17)
        • 깊이 우선 탐색, 너비 우선 탐색[DFS, BFS.. (34)
        • 재귀[Recursion] (2)
        • 백트래킹[Backtracking] (5)
        • 브루트포스 알고리즘[Bruteforce] (16)
        • 자료 구조[Data Structure] (4)
        • 분할 정복 알고리즘[Divide & Conquer.. (3)
      • CS (30)
      • Network (11)
      • Database (7) N
        • Elasticsearch (3)
      • Linux (3)
      • JavaScript (4)
        • AngularJS (1)
      • Java (103)
        • Effective Java (9)
        • Java Concept (22)
        • Spring (63)
        • Design Pattern (4)
      • Python (2)
      • Vscode (1)
      • DevOps (44)
        • AWS (27)
        • Git (7)
        • Docker (7)
        • Nginx (1)
      • 자격증 (10)
        • SQL (4)
      • 사이드 프로젝트 (3)
        • MatJido (3)
      • 기타 (9)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 소개
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    AWS
    백트래킹
    컴퓨터구조
    Effective Java
    메타코딩
    docker
    Spring
    정보처리기사
    Wi-Fi
    정보처리기사 실기
    dp
    너비 우선 탐색
    dfs
    sqld
    이펙티브 자바
    다이나믹 프로그래밍
    BFS
    일본여행
    정보처리기사 필기
    springboot
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
SeungbeomKim
[Java] gc에서 사용하지 않는 객체를 어떻게 구분하는가
상단으로

티스토리툴바