NDM

[Java] GC와 Java Reference Type 1편 : Java Reference Type 본문

Java

[Java] GC와 Java Reference Type 1편 : Java Reference Type

ndm.jr 2022. 5. 1. 18:51

목차

  • Java Reference Type
    • Strong Reference
    • Weak Reference
    • Soft Reference
    • Phantom Reference

 

Java Reference Type에 대해 학습한다.

다음 시간에 알아볼 GC를 보다 효과적으로 학습하기 위해서는 알아야할 필요가 있다고 판단했기 때문이다.


Java Reference는 총 4가지 유형이 존재한다.
  • Strong Reference
  • Weak Reference
  • Soft Reference
  • Phantom Reference

 

GC는 공통적으로 다음의 과정을 수행한다
  1. GC 대상이되는객체(Garbage)들을찾는다
  2. 찾아낸 Garbage를처리한다
  3. 처리한만큼의 Heap 메모리를회수한다

 

GC의 대상이 되는 객체들을 찾기 위해서 Java GC는 Reachability라는 개념을 사용한다
  • "유효한 참조"가존재한다면 Reachable
  • RootSet(최초의참조) 으로부터 참조가 이어지는 객체사슬이 유지되는경우
  • 없다면 UnReachable

 

왜 Java Reference Type이라는게 존재하게 되었을까?

  • Java에서는 프로그램을 작성하다 더이상 쓸모 없어진 객체에 대하여 C++처럼 일일히 메모리 해제를 해주지 않는다.
    GC가 알아서 참조가 사라진 객체에 대하여 메모리를 해제해주기 때문이다.
  • 이처럼 최초의 Java 환경에서는 GC(Garbage Collection)에 사용자의 코드가 관여되지 못하도록 설계되어 있었다.
  • 하지만 JDK1.2부터 사용자 코드와 GC가 조금이나마 상호작용을 할 수 있도록 java.lang.ref 패키지를 추가하게 되었고, 이로 인해 Java Reference Type이 나눠지게 되었다. (물론 마음대로 GC를 수동으로 동작시켜도 된다는 이야기는 아니다)

1. Strong Reference

  • 다음을 보면 참조는총 4가지유형으로이루어진다
  • 여기서 "Heap내의 다른객체에 의한 참조"을 제외한 나머지 3개가 RootSet에속한다 
유형 RootSet
Heap내의 다른객체에 의한 참조 X
Stack으로부터의참조(파라미터, 지역변수등에의한참조) O
Method Area에의한참조(Static) O
JNI로부터의참조 O

  • RootSet으로부터 객체사슬을 이루는 객체들은 모두 Reachable이며, GC의대상이아니다
  • 나머지 RootSet으로부터 이어지는 객체사슬을 이루지 못한 객체들은 UnReachable이며, GC의대상이된다
  • 단, 여기서 검은색 동그라미를 친 객체는 객체사슬을 이루는 것 같지만 UnReachable이다
    • Reachable한 객체를 "참조하고는" 있으나 다른 Reachable한 객체가 해당 객체를 참조하고 있지는 않기 때문
 
  • 이것은 자바의 일반적인 참조관계이며, 이를 Strong Reference라고한다.

 


2. Weak Reference(java.lang.ref)

  • WeakReference 라는 클래스의 형태로 제공된다. ( soft, phantom도 마찬가지다 )
  • 다른 클래스를 캡슐화하여 생성할수있으며, 캡슐화된 내부객체는 WeakReference에 의해 참조된다.
    이와 같이 생성된 객체는(soft, phantom 포함) Java GC에서 특별하게 취급한다.

여기서 ex 변수에 null이라는 값을 대입하면 참조관계가 이렇게 바뀐다

Sample객체를 가리키던 ex변수에 null이 대입되면서,
Sample객체는 오직 WeakReference객체에 의해서만 참조되게 되었다. 이를 Weak Reachable 상태라고 한다.

 


Reachable이 정확히 뭐지? ( feat. WeakReference )

Strong Reference만 존재할 때는 다음과 같은 특징이 있었다.
  • 상태는 Reachable과 UnReachable 상태만존재한다.
  • GC대상 여부를 판별하는 부분에 사용자의 개입은 존재하지않는다

하지만 java.lang.ref 패키지의 WeakReference, SoftReference, PhantomReference가 들어오고 나서 이렇게 바뀌었다.

  • strongly reachable, softly reachable, weakly reachable, phantomly reachable 에 따라 별개의 GC동작을 지정해 줄 수 있다. -> GC에 사용자 코드가 일부 개입할 수 있게 되었다.

  • GC가 동작할때, Weakly Reachable도 Garbage로 간주되어 메모리에서 회수된다.
  • 객체사슬에 포함되어 있지만 회수되므로,
    참조는 가능하지만 반드시 유효할 필요가 없는 임시객체들을 저장하는 구조를 만들수있다. Ex) LRU캐시
  • 주목해야할것은, WeakReference 객체자체는 Strong Reachable객체이다.
    • WeakReference(Strong : GC대상X)객체가 참조하는 객체는 Weakly Reachable(Weak : GC대상)이다.
  • 또한 WeakReference와 RootSet이 동시에 참조한다면 Strong Reachable이다.(A)
  • GC가 동작할때 "나"객체를 Weakly Reachable로 판명한다면 "가"의 "나"에대한참조를 Null로 바꿔버린다.
    • 즉 Weakly Reachable 상태는 UnReachable 상태와 다름 없게 되어 GC의 대상이 된다.

3. Soft Reference

  • "오직 SoftReference객체로만 참조된 객체"는 Heap의 메모리크기와 해당객체의 사용빈도에 따라 GC여부가결정된다. 이를 Soft Reachble 상태 라고 한다.
  • 때문에 Weakly Reachable 객체와 달리 GC가 동작할때 마다 회수되지 않으며, 자주 사용될수록 오래 살아남는다.
  • 이 수식에 따라 Soft Reference 의 GC여부가 결정된다.
    • (마지막 strong reference가 GC된 때로부터 지금까지의 시간) > (옵션 설정값 N) * (힙에 남아있는 메모리 크기)
    • 옵션은 JVM의 -XX:SoftRefLRUPolicyMSPerMB=<N> 옵션을 뜻한다. Default는 1000이다

 

Soft Reference와 Weak Reference의 가장 큰 차이점은 "GC가 동작할 때 마다 삭제 대상이 되는가"의 차이

Soft Reference Weak Reference
GC가 동작하는 것과 상관 없음 GC가 동작할때 마다 GC의 대상이 됨

 

중요한게 또 한가지 있다. Soft Reference 와 Weak Reference의 차이가 "GC 대상"의 차이라고 했다.

그리고 GC는 맨 위에서 설명한 것 처럼 "GC 대상 판별 -> 처리 -> Heap 메모리 회수"의 순서를 거친다고 했다.

이 말은 "GC대상이 되는 것"과 "Heap의 메모리가 회수되는 것"은 별개의 일이라는 것이다.

왜냐하면 GC 알고리즘마다 다르고, GC의 대상이된 객체의 메모리를 한번에 회수하지도 않기 때문이다.

 


Reference Queue와 PhantomReference

아까의 그림을 다시 보겠다

  1. "나"객체가 GC에 의해 Weakly Reachable 판정이 났다. 그러면 "가" 에서 "나"에 대한 참조가 Null로 바뀐다고 했다.
  2. 그러면 "나"는 UnReachable이 되어 GC의 대상이된다. 여기서 "가"객체, 즉 WeakReference 객체 자체는ReferenceQueue에 삽입된다.
  3. 이제 ReferenceQueue의 메서드를 이용해 객체들을 확인해보면, 객체가 GC대상이 되었는지를 알 수 있을것이고, 이에 따른 리소스에 대한 후처리가 가능해진다.
  4. Java Collection의 WeakHashMap이 이 ReferenceQueue와 WeakReference 객체를 사용해 만들어져있다.

 

여기서 Weak, Soft, Phantom중 유일하게 Phantom만 필수적으로 ReferenceQueue를 사용해야 한다는 특징이 있다.

(생성자가 ReferenceQueue를 사용하는 경우 딱 하나다)

 

Weak나 Soft같은 경우는 이와 같은 순서로 진행된다.

  • 내부 객체 참조를 Null로 변경 -> WeakReference/SoftReference 객체를 ReferenceQueue에 삽입

하지만 Phantom같은 경우 아래와 같은 순서로 진행된다.

  • 내부 객체 참조를 Null로 변경하지 않음 -> 참조된 객체 Phantomly Reachable 상태 -> PhantomReference객체를 ReferenceQueue에 삽입

즉, PhantomReference는 "객체가 처리된 후" 필요한 작업들을 수행할 수 있다는 특징이 있다.

다시말하면, Weak/Soft는 "GC대상이 되는가" / Phantom은 "객체가 처리된 후"에 포커싱이 맞춰져있다는 이야기다

맨 처음 말했던 GC의 동작 과정을 다시 보겠다(각각의 과정은 연속된 작업이 아니라고 했다)

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

작업은 항상 GC대상 판별 -> 처리 -> Heap 메모리 회수로 이어지며

GC대상 판별 단계에서는 Strong -> soft -> weak의 순서로 판별한다.

모두 아니라면 파이널라이즈(객체 처리)를 진행한다.

그리고 대상 객체를 참조하는 PhantomReference 객체가 있다면, PhantomReference를 ReferenceQueue에 삽입한다

그리고 다시 파이널라이즈를 진행하고, 메모리 회수는 지연시킨다.

 

주의점은 phantomly Reachable로 한번 판명된 객체는 두번 다시 사용할 수 없으며, GC가 알아서 Null로 바꿔주지도 않는다. 때문에 사용자가 명시적으로 Null로 설정해야 메모리 회수가 진행된다.


 

출처

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