Java의 WeakHashMap 및 캐싱 : 값이 아닌 키를 참조하는 이유는 무엇입니까?
Java의 WeakHashMap 은 종종 캐싱에 유용한 것으로 인용됩니다. 약한 참조가 값이 아닌 맵의 키로 정의된다는 것은 이상하게 보입니다. 내 말은, 캐시하고 싶은 값이고, 캐시 외에 다른 누구도이 값을 강력하게 참조하지 않으면 가비지 수집을 원합니다.
키에 대한 약한 참조를 유지하는 데 어떤 방식으로 도움이됩니까? 를하면 ExpensiveObject o = weakHashMap.get("some_key")호출자가 더 이상 강력한 참조를 보유하지 않을 때까지 캐시가 'o'를 유지하기를 원하며 문자열 개체 "some_key"에 대해서는 전혀 신경 쓰지 않습니다.
내가 뭔가를 놓치고 있습니까?
WeakHashMap 은 적어도 대부분의 사람들이 생각하는 방식으로는 캐시로 유용 하지 않습니다 . 당신이 말했듯이, 그것은 약한 값이 아닌 약한 키 를 사용하므로 대부분의 사람들이 그것을 사용하고 싶어하는 용도로 설계되지 않았습니다 (사실 사람들이 그것을 잘못 사용하는 것을 보았습니다 ).
WeakHashMap은 수명주기를 제어 할 수없는 객체에 대한 메타 데이터를 유지하는 데 주로 유용합니다. 예를 들어, 클래스를 통과하는 많은 객체가 있고 범위를 벗어 났을 때 알림을받을 필요가없고 참조없이 해당 객체에 대한 추가 데이터를 계속 추적하려는 경우입니다.
간단한 예 (그리고 이전에 사용한 적이있는 예)는 다음과 같습니다.
WeakHashMap<Thread, SomeMetaData>
시스템의 다양한 스레드가 수행하는 작업을 추적 할 수있는 위치 스레드가 죽으면 항목이 맵에서 자동으로 제거되고 스레드에 대한 마지막 참조 인 경우 스레드가 가비지 수집되는 것을 방지 할 수 없습니다. 그런 다음 해당 맵의 항목을 반복하여 시스템의 활성 스레드에 대한 메타 데이터를 찾을 수 있습니다.
캐시가 아닌 WeakHashMap을 참조하십시오 ! 자세한 내용은.
원하는 캐시 유형에 대해 전용 캐시 시스템 (예 : EHCache )을 사용하거나 google-collections의 MapMaker 클래스를 살펴보세요 . 뭔가
new MapMaker().weakValues().makeMap();
당신이 원하는 것을 할 것입니다. 또는 당신이 멋지게 만들고 싶다면 시간 만료를 추가 할 수 있습니다.
new MapMaker().weakValues().expiration(5, TimeUnit.MINUTES).makeMap();
의 주요 용도 WeakHashMap는 키가 사라질 때 사라지고 싶은 매핑이있을 때입니다. 캐시는 그 반대입니다. 값이 사라지면 사라지고 싶은 매핑이 있습니다.
캐시의 경우 원하는 것은 Map<K,SoftReference<V>>. A SoftReference는 메모리가 부족할 때 가비지 수집됩니다. ( WeakReference이를 참조 대상에 대한 하드 참조가 더 이상없는 즉시 지워질 수 있는와 대조하십시오 .) 참조가 캐시에서 소프트되기를 원합니다 (최소한 키-값 매핑이 오래되지 않는 경우). ), 이후 나중에 찾을 경우 값이 여전히 캐시에있을 가능성이 있습니다. 대신 참조가 약한 경우 값이 즉시 gc되어 캐싱 목적이 무효화됩니다.
편의를 위해, 당신은 숨길 수 SoftReference귀하의 내부 값을 Map캐시 타입의 것으로 나타나도록 구현 <K,V>대신 <K,SoftReference<V>>. 그렇게하려면 이 질문 에 인터넷에서 사용할 수있는 구현에 대한 제안 이 있습니다.
참고 또한 사용할 때 SoftReferenceA의 값을 Map, 당신이 있어야합니다 수동으로 있었다 키 - 값 쌍을 제거하려면 뭔가 할 SoftReferences그렇지 않으면 --- 지워 당신은 Map단지 영원히 크기가 증가하고, 누수 메모리 것입니다.
고려해야 할 또 다른 사항은 Map<K, WeakReference<V>>접근 방식 을 취 하면 값이 사라질 수 있지만 매핑은 그렇지 않다는 것입니다. 사용법에 따라 결과적으로 Weak References가 GC 처리 된 많은 항목을 포함하는 Map이 표시 될 수 있습니다.
두 개의 맵이 필요합니다. 하나는 캐시 키와 약한 참조 값을 매핑하고 다른 하나는 약한 참조 값과 키를 반대 방향으로 매핑합니다. 그리고 참조 대기열 과 정리 스레드 가 필요합니다 .
약한 참조는 참조 된 객체에 더 이상 액세스 할 수 없을 때 참조를 대기열로 이동할 수있는 기능이 있습니다. 이 큐는 정리 스레드에 의해 비워 져야합니다. 그리고 정리를 위해 참조 용 키를 가져와야합니다. 이것이 두 번째 맵이 필요한 이유입니다.
다음 예제는 약한 참조의 해시 맵을 사용하여 캐시를 생성하는 방법을 보여줍니다. 프로그램을 실행하면 다음과 같은 출력이 표시됩니다.
$ javac -Xlint : 체크되지 않은 Cache.java && 자바 캐시
{짝수 : [2, 4, 6], 홀수 : [1, 3, 5]}
{짝수 : [2, 4, 6]}
첫 번째 줄은 홀수 목록에 대한 참조가 삭제되기 전의 캐시 내용과 확률이 삭제 된 후의 두 번째 줄을 보여줍니다.
다음은 코드입니다.
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Cache<K,V>
{
ReferenceQueue<V> queue = null;
Map<K,WeakReference<V>> values = null;
Map<WeakReference<V>,K> keys = null;
Thread cleanup = null;
Cache ()
{
queue = new ReferenceQueue<V>();
keys = Collections.synchronizedMap (new HashMap<WeakReference<V>,K>());
values = Collections.synchronizedMap (new HashMap<K,WeakReference<V>>());
cleanup = new Thread() {
public void run() {
try {
for (;;) {
@SuppressWarnings("unchecked")
WeakReference<V> ref = (WeakReference<V>)queue.remove();
K key = keys.get(ref);
keys.remove(ref);
values.remove(key);
}
}
catch (InterruptedException e) {}
}
};
cleanup.setDaemon (true);
cleanup.start();
}
void stop () {
cleanup.interrupt();
}
V get (K key) {
return values.get(key).get();
}
void put (K key, V value) {
WeakReference<V> ref = new WeakReference<V>(value, queue);
keys.put (ref, key);
values.put (key, ref);
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append ("{");
boolean first = true;
for (Map.Entry<K,WeakReference<V>> entry : values.entrySet()) {
if (first)
first = false;
else
str.append (", ");
str.append (entry.getKey());
str.append (": ");
str.append (entry.getValue().get());
}
str.append ("}");
return str.toString();
}
static void gc (int loop, int delay) throws Exception
{
for (int n = loop; n > 0; n--) {
Thread.sleep(delay);
System.gc(); // <- obstinate donkey
}
}
public static void main (String[] args) throws Exception
{
// Create the cache
Cache<String,List> c = new Cache<String,List>();
// Create some values
List odd = Arrays.asList(new Object[]{1,3,5});
List even = Arrays.asList(new Object[]{2,4,6});
// Save them in the cache
c.put ("odd", odd);
c.put ("even", even);
// Display the cache contents
System.out.println (c);
// Erase one value;
odd = null;
// Force garbage collection
gc (10, 10);
// Display the cache again
System.out.println (c);
// Stop cleanup thread
c.stop();
}
}
'program story' 카테고리의 다른 글
| 두 개체가 한 줄로 선언 된 경우 어떤 순서로 구성됩니까? (0) | 2020.11.23 |
|---|---|
| 메서드에서 익명 유형을 반환하는 방법이 있습니까? (0) | 2020.11.23 |
| CORBA가 인기를 잃은 이유는 무엇입니까? (0) | 2020.11.23 |
| ASP.NET MVC : Razor의 사용자 지정 Html 도우미 (0) | 2020.11.23 |
| “패키지 'android'의 'showAsAction'속성에 대한 리소스 식별자가 없습니다.” (0) | 2020.11.23 |