Java에는 StrongReference, SoftReference, WeakReference 및 PhantomReference(전설적인 유령 참조 ㅋㅋㅋ)의 4가지 유형의 참조가 있습니다.
이 네 가지 유형의 참조는 GC와 밀접한 관련이 있으며 해당 정의 및 사용 시나리오를 하나씩 살펴보겠습니다.
1. 강력한 참조
StrongReference는 Java의 기본 참조 구현으로, 이를 가리키는 객체가 없으면 GC 실행 후 재활용됩니다.
자바 코드
다음과 같이 코드 코드를 복사합니다 .
@시험
공개 무효 StrongReference() {
객체 참조 = new Object();
/**
* 할당을 통해 StrongReference 생성
*/
객체 StrongReference = 참조대상;
주장Same(참조 대상, 강한 참조);
대상 = null;
시스템.gc();
/**
* StrongReference는 GC 이후에 재활용되지 않습니다.
*/
주장NotNull(strongReference);
}
2. WeakReference 및 WeakHashMap
WeakReference는 이름에서 알 수 있듯이 약한 참조입니다. 참조된 개체가 더 이상 JVM에 강한 참조를 갖지 않으면 약한 참조는 GC 후에 자동으로 재활용됩니다.
다음과 같이 코드 코드를 복사합니다 .
@시험
공개 무효 약한 참조() {
객체 참조 = new Object();
WeakReference<Object> WeakRerference = new WeakReference<Object>(참조 대상);
주장Same(참조, 약한Rerference.get());
대상 = null;
시스템.gc();
/**
* 참조 대상을 가리키는 강한 참조가 없으면 약한 참조는 GC 이후 자동으로 재활용됩니다.
*/
주장Null(weakRerference.get());
}
WeakHashMap은 WeakReference를 키로 사용합니다. 키에 대한 강력한 참조가 없으면 WeakHashMap은 GC 후에 관련 항목을 자동으로 삭제합니다.
다음과 같이 코드 코드를 복사합니다 .
@시험
공개 무효weakHashMap()은 InterruptedException을 발생시킵니다.
Map<Object, Object> WeakHashMap = new WeakHashMap<Object, Object>();
객체 키 = new Object();
객체값 = new Object();
WeakHashMap.put(키, 값);
주장True(weakHashMap.containsValue(값));
키 = null;
시스템.gc();
/**
* 다음에 getTable이 호출될 때 해당 항목이 지워질 수 있도록 유효하지 않은 항목이 ReferenceQueue에 들어갈 때까지 기다립니다.
*/
Thread.sleep(1000);
/**
* 키에 대한 강력한 참조가 없으면 WeakHashMap은 GC 이후 관련 항목을 자동으로 삭제합니다.
*/
주장False(weakHashMap.containsValue(값));
}
3.소프트레퍼런스
SoftReference는 기본적으로 WeakReference와 동일한 특성을 가지고 있습니다. 가장 큰 차이점은 SoftReference가 재활용되기 전에 JVM의 메모리가 부족해질 때까지 가능한 한 오랫동안 참조를 유지한다는 것입니다(가상 머신 보장). 이 기능은 SoftReference를 캐싱 애플리케이션에 매우 적합하게 만듭니다.
다음과 같이 코드 코드를 복사합니다 .
@시험
공개 무효 SoftReference() {
객체 참조 = new Object();
SoftReference<Object> SoftRerference = new SoftReference<Object>(참조대상);
assertNotNull(softRerference.get());
대상 = null;
시스템.gc();
/**
* 소프트 참조는 jvm OutOfMemory 이전에만 재활용되므로 캐싱 애플리케이션에 매우 적합합니다.
*/
assertNotNull(softRerference.get());
}
4. 팬텀레퍼런스
이 기사의 주인공인 Phantom Reference는 WeakReference 및 SoftReference와 매우 다릅니다. 그 이유는 get() 메서드가 항상 null을 반환하기 때문입니다. 여기서 이름이 유래되었습니다.
자바 코드
다음과 같이 코드 코드를 복사합니다 .
@시험
공공 무효 팬텀ReferenceAlwaysNull() {
객체 참조 = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(참조대상, new ReferenceQueue<Object>());
/**
* 팬텀 참조의 get 메소드는 항상 null을 반환합니다.
*/
주장Null(phantomReference.get());
}
항상 null을 반환하는 참조의 용도는 무엇입니까? PhantomReference를 구성할 때 두 번째 매개변수인 ReferenceQueue에 주의하세요(사실 WeakReference 및 SoftReference도 이 매개변수를 가질 수 있습니다).
PhantomReference의 유일한 용도는 참조대상이 ReferenceQueue에 대기열에 추가되는 시기를 추적하는 것입니다.
5. 관련성 대기열
WeakReference가 null을 반환하기 시작하면 해당 개체가 재활용될 준비가 된 것입니다. 이때 몇 가지 적절한 정리 작업을 수행할 수 있습니다. 개체가 재활용되면 가상 머신이 참조 생성자에 전달됩니다. 객체가 ReferenceQueue에 삽입됩니다. WeakHashMap은 ReferenceQueue를 사용하여 키에 더 이상 강력한 참조가 없는 항목을 지웁니다.
자바 코드
다음과 같이 코드 코드를 복사합니다 .
@시험
public void referenceQueue()가 InterruptedException을 발생시킵니다.
객체 참조 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
WeakReference<Object> WeakReference = new WeakReference<Object>(referent, referenceQueue);
주장False(weakReference.isEnqueued());
Reference<?extends Object> polled = referenceQueue.poll();
주장Null(폴링);
대상 = null;
시스템.gc();
주장True(weakReference.isEnqueued());
참조<? 확장 개체> 제거됨 = referenceQueue.remove();
주장NotNull(삭제);
}
6. PhantomReference 대 WeakReference
PhantomReference에는 두 가지 이점이 있습니다. 첫째, 객체가 메모리에서 삭제되는 시기를 정확히 알 수 있습니다. 이 기능은 일부 특별한 요구 사항에 사용할 수 있습니다(예: 분산 GC, XWork 및 google-guice도 일부 정리 작업을 수행했습니다).
둘째, 마무리로 인해 발생하는 몇 가지 근본적인 문제를 피할 수 있습니다. 위에서 언급한 것처럼 PhantomReference의 유일한 기능은 참조 항목이 ReferenceQueue에 추가되는 시점을 추적하는 것입니다. 그러나 WeakReference에도 해당 기능이 있습니다.
이것은 Object의 finalize 메소드에 관한 것입니다. 이 메소드는 gc가 실행되기 전에 호출됩니다. 객체가 finalize 메소드를 오버로드하고 의도적으로 메소드 내에서 자신에 대한 강력한 참조를 생성하면 이번 GC가 불가능해집니다. 이 객체는 GC를 유발할 수 있습니다. 최종 결과는 JVM에 많은 가비지가 있지만 OutOfMemory가 있다는 것입니다. PhantomReference가 마무리 중이므로 이 문제를 피할 수 있습니다. 메소드가 실행된 후에 재활용된다는 것은 이때 원본 참조를 얻는 것이 불가능하다는 것을 의미하므로 위의 문제는 발생하지 않을 것입니다. 물론 이는 매우 극단적인 예이며 일반적으로 발생하지 않습니다.
7. 비교
소프트 vs 약 vs 팬텀 참조 | ||||
---|---|---|---|---|
유형 | 목적 | 사용 | GCed일 때 | 클래스 구현 |
강력한 참조 | 일반 참조는 참조되는 한 객체를 활성 상태로 유지합니다. | 정상적인 참조. | 지정되지 않은 모든 개체는 회수될 수 있습니다. | 기본 |
소프트 참조 | 메모리가 충분하다면 객체를 활성 상태로 유지합니다. | 클라이언트가 참조(메모리에 민감한 캐시)를 제거한 후에도 클라이언트가 키로 해당 참조를 다시 요청하기 시작할 경우를 대비해 객체를 활성 상태로 유지합니다. | 첫 번째 gc 패스 이후 JVM은 여전히 더 많은 공간을 확보해야 한다고 결정합니다. | java.lang.ref.SoftReference |
약한 참조 | 클라이언트가 사용 중(연결 가능한) 동안에만 개체를 활성 상태로 유지합니다. | 더 이상 사용하지 않는 객체를 자동으로 삭제하는 컨테이너입니다. | gc가 객체에 약하게만 접근 가능하다고 판단한 후 | java.lang.ref.WeakReference java.util.WeakHashMap |
팬텀 레퍼런스 | 마무리 후 공간이 회수되기 전에 정리할 수 있습니다(finalize() 사용을 대체하거나 확대). | 특수 클린업 처리 | 마무리 후. | java.lang.ref.PhantomReference |
8. 요약
일반 애플리케이션에는 참조 프로그래밍이 포함되지 않지만, 이 지식을 이해하면 GC의 작동 원리와 성능 튜닝을 이해하는 데 도움이 될 것입니다. 캐싱과 같은 몇 가지 기본 기능을 구현할 때도 도움이 될 수 있기를 바랍니다.