제리의 배움 기록

[Java] Reference Type : 참조유형 본문

자바

[Java] Reference Type : 참조유형

제리92 2021. 11. 12. 10:40

자바는 효율적은 GC를 위해 4가지의 참조유형을 다룹니다.

적절한 Reference를 사용하면 GC 대상에 우선순위를 적용하여 메모리를 효율적으로 관리할 수 있습니다.

자바의 참조유형

  1. Strong Reference : 일반적인 기본 참조 유형
  2. Soft Reference
  3. Weak Reference
  4. Phantom Reference

SoftReference, WeakReference, PhantomReference 3가지 클래스에 의해 생성된 객체를 reference object 라고 합니다.

하나의 객체는 여러 reference 조합으로 참조될 수 있습니다. reference 타입 구분은 root set으로 부터를 기준으로 합니다.

  • strongly reachable: root set으로부터 시작해서 객체까지 도달하는 여러 참조 사슬 중 reference object가 아닌 참조가 하나라도 있는 객체
  • softly reachable: strongly reachable 이 아닌 객체 중에서(Strong Reference가 없는) weak reference, phantom reference 없이 soft reference만 통과하는 참조 사슬이 하나라도 있는 객체
  • weakly reachable: strongly reachable, softly reachable이 아닌 객체 중에서, phantom reference 없이 weak reference만 통과하는 참조가 하나라도 있는 객체
  • phantomly reachable: strongly reachable, softly reachable, weakly reachable 모두 해당되지 않는 객체. 이 객체는 파이널라이즈(finalize)되었지만 아직 메모리가 회수되지 않은 상태
  • unreachable: root set으로부터 시작되는 참조 사슬로 참조되지 않는 객체

GC 과정

GC 대상 객체를 찾는 작업 -> GC 대상 객체를 처리(파이널라이즈) -> 할당된 메모리를 회수

연속된 작업은 아님

GC가 객체를 처리하는 순서

  1. soft references
  2. weak references
  3. 파이널라이즈
  4. phantom reference
  5. 메모리회수

ReferenceQueue

어떤 객체가 더 이상 필요없게 되었을 때 관련 후처리를 해야할 경우 유용하게 사용할 수 있음

Soft Reference

  • as late as possible : 메모리 여유가 충분하다면 GC가 수행되더라고 수거되지 않습니다.
  • 객체 생성시 RefereceQueue를 생성자 인자로 넘기냐에 따라 사용유무가 정해집니다.
  • 객체 내부의 참조가 null로 설정된 후 ReferenceQueue에 enqueue
  • => 이 과정은 GC에 의해 자동으로 수행됨
MyClass ref = new MyClass();      

SoftReference<MyClass> softRef = new SoftReference<MyClass>(ref); 

// 이 시점에 GC의 실행 대상이 가능 
ref = null;

// JVM의 메모리가 부족하지 않아 GC 실행 대상이 되지 않은 경우 null이 반환되지 않고 기존 객체가 반환됨
ref = softRef.get();

Weak Reference

  • GC가 수행될때마다 회수 대상 (실제 GC 알고리즘에 따라 반드시 회수된다고 보장할 수 없음)
  • 짧은 주기에 자주 사용되는 객체를 캐시할 때 유용
  • 객체 생성시 RefereceQueue를 생성자 인자로 넘기냐에 따라 사용유무가 정해집니다.
  • 객체 내부의 참조가 null로 설정된 후 ReferenceQueue에 enqueue
  • => 이 과정은 GC에 의해 자동으로 수행됨
MyClass ref = new MyClass();      

WeakReference<MyClass> weakRef = new WeakReference<MyClass>(ref); 

// 이 시점에 GC의 실행 대상이 가능 
ref = null;

// 다음 GC 실행시 힙 메모리에서 제거 제거된 경우 null 반환
ref = weakRef.get();

Phantom Reference

  • 객체 내부의 참조를 null로 설정하지 않고 참조된 객체를 phantomly reachable 객체로 만든 이후에 ReferenceQueue에 enqueue
    • 객체에 대한 참조가 PhantomReference만 남게 되면 해당 객체는 바로 파이널라이즈
    • 파이널라이즈 된 이후에 phantomly reachable로 간주하여 ReferenceQueue에 enqueue
  • 파이널라이즈 이후 작업을 애플리케이션이 수행하게 하고 메모리 회수는 지연
  • PhantomReference는 반드시 ReferenceQueue를 사용해야함.
    => 이를 통해 객체의 파이널라이즈 이후 필요한 작업들을 처리
  • phantomly reachable 객체는 PhantomeReference get() 매소드에서 항상 null을 반환 하므로 더이상 사용할 수 없음
  • 객체에 대한 참조를 GC가 자동으로 null로 설정하지 않으므로 후처리 작업 후 사용자 코드에서 명시적으로 clear() 매소드를 실행하여 null로 설정해야 메모리 회수가 진행됨
@Test
    public void test() {
        MyClass ref = new MyClass();

        ReferenceQueue<MyClass> refQueue = new ReferenceQueue<MyClass>();

        PhantomReference<MyClass> phantomRef = null;

        // 팬텀 참조는 ReferenceQueue를 반드시 사용해야함
        phantomRef = new PhantomReference<MyClass>(ref,refQueue);

        ref = null;

        System.gc();

        System.out.println(phantomRef.isEnqueued());

        System.out.println(phantomRef.get()); // null

    }

 

[참고]

Naver D2 - Java Reference와 GC

Comments