WeakHashMap 깊이 들 어가 기

9059 단어
WeakHashMap
       최근 작업 중 자바 util. Weak HashMap < K, V >, 그 속 의 오묘 함 을 이해 하지 못 하고 결말 을 찾 아 기록 하 다.
       
Java 참조 형식
우선 자바 의 네 가지 인용 유형 을 알 아야 합 니 다.
  • 강 인용 (StrongReference)
  • 강 한 인용 은 가장 보편적 인 인용 을 사용 하 는 것 이다. 평소에 우리 가 자주 쓰 는 A = new A ().억지로 인용 하 다
    GC 는 강력 한 인용 을 회수 하지 않 습 니 다. 메모리 가 부족 하 더 라 도 그렇지 않 습 니 다. 차라리 OOM 하 겠 습 니 다.
  • 소프트 인용 (소프트 참조)        

  • SoftReference 의 주요 특징 은 비교적 강 한 인용 기능 을 가지 고 있다 는 것 이다.
    메모리 가 부족 할 때 만 회수 하고 메모리 가 충분 할 때 는 회수 되 지 않 는 다.
    또한 인용 대상 은 자바 가 OutOfmory Error 를 던 지기 전에 null 로 설정 할 수 있 도록 보장 합 니 다.
    소프트 인용 사용 은 guava - cache 를 참고 할 수 있 습 니 다.
  • 약 인용 (WeakReference)
  • Weak Reference 는 SoftReference 가 필요 로 하 는 조건 이 아 닌 쓰레기 수 거 기 를 실행 할 때 반드시 수 거 됩 니 다.그러나 대상 의 인용 관계 가 복잡 하 다 면 여러 번 회수 해 야 목적 을 달성 할 수 있 을 것 이다.
  • 허 인용 (PhantomReference)
  • Phantom Reference 는 주로 finalize 방법 을 보조 하 는 데 쓰 인 다.
    PhantomReference 대상 이 finalize 방법 을 실행 한 후 Unreachable Objects 가 됩 니 다.
    그러나 아직 회수 되 지 않 았 기 때문에 이때 finalize 를 보조 하여 후기 회수 작업 을 할 수 있다.    
    Weak HashMap 에서 Weak 를 어떻게 실현 합 니까?
    다 들 아시 다시 피 Map 은 Entry 로 데 이 터 를 저장 하고 Weak HashMap 의 세부 사항 을 살 펴 보 세 요.
    Put 부분의 코드 는 다음 과 같 습 니 다.
    Entry<K,V> e = tab[i];
             tab[i] = new Entry<K,V>(k, value, queue, h, e);
             if (++size >= threshold)
                 resize(tab.length * 2);

    다음은 Weak HashMap 의 Entry 실현, Weak Reference 계승
    private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
                     private V value;
                     private final int hash;
                     private Entry<K,V> next;
                     /**
                      * Creates new entry.
                      */
                     Entry(K key, V value,
                       ReferenceQueue<K> queue,
                           int hash, Entry<K,V> next) {
                         super(key, queue);
                         this.value = value;
                         this.hash  = hash;
                         this.next  = next;
                     }

    Key 를 Reference 로 처리 하기:
    Reference(T referent, ReferenceQueue<? super T> queue) {
                 this.referent = referent;
                 this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
                 }

    HashMap 과 비교 해 보면 Entry 는 Key 라 는 대상 을 직접 인용 하지 않 고 인용 관 계 를 부류 Weak Reference 에 넣 었 다. Weak HashMap 은 들 어 오 는 key 를 Weak Reference 로 포장 하고 Reference Queue 에 들 어 온 것 을 알 수 있다.그러나 약 한 인용의 실현 세부 사항 은 아직 명확 하지 않 으 니 이어서 파 헤 쳐 라.
    키 는 어떻게 정리 합 니까?
    Reference 클래스 에 static 코드 가 있 습 니 다.        
    static private class Lock { };
     private static Lock lock = new Lock();  
     private static Reference pending = null;
             
     static {
         ThreadGroup tg = Thread.currentThread().getThreadGroup();
         for (ThreadGroup tgn = tg;
              tgn != null;
              tg = tgn, tgn = tg.getParent());
             Thread handler = new ReferenceHandler(tg, "Reference Handler");
             /* If there were a special system-only priority greater than
              * MAX_PRIORITY, it would be used here
              */
             handler.setPriority(Thread.MAX_PRIORITY);
             handler.setDaemon(true);
             handler.start();
     }

    스 레 드 의 우선 순 위 를 MAX 로 설정 합 니 다. 어떤 스 레 드 가 이렇게 높 은 권한 을 필요 로 합 니까?pending 、lock 은 모두 static 에 의 해 설명 되 었 습 니 다. lock. wait 이후 에 누가 깨 웠 는 지 인터넷 에서 검색 을 한 후에 야 JVM 이 이런 일 에 참여 했다 는 것 을 알 게 되 었 습 니 다.   
    통속 적 인 말로 JVM 이 한 일 을 연결 하 다.   Weak HashMap 대상 에 많은 대상 의 인용 이 저장 되 어 있다 고 가정 합 니 다.JVM 은 CMS GC 를 진행 할 때 ConcurrentMarkSweetThread (CMST 로 약칭) 스 레 드 를 만들어 GC 를 진행 합 니 다. ConcurrentMarkSweetThread 스 레 드 가 생 성 되 는 동시에 SurrogateLockerThread (SLT 로 약칭) 스 레 드 를 만 들 고 시작 합 니 다. SLT 가 시작 되면 대기 단계 에 있 습 니 다.CMST 가 GC 를 시작 할 때 SLT 에 자바 계층 Reference 대상 의 전역 잠 금 을 가 져 오 라 는 메 시 지 를 보 냅 니 다: lock.JVM 은 CMS GC 가 완 료 될 때 까지 Weak HashMap 에서 회수 대상 이 속 한 Weak Reference 용기 대상 을 Reference pending 에 넣 습 니 다. 속성 중 (매번 GC 가 끝 난 후 pending 속성 은 기본적으로 null 이 되 지 않 습 니 다), 그리고 SLT 에 게 방출 을 알 리 고 notify 전역 잠 금: lock.이때 Reference Handler 스 레 드 의 run 방법 을 활성화 하여 wait 상태 에서 벗 어 나 작업 을 시작 합 니 다.Reference Handler 라 는 스 레 드 는 pending 의 모든 Weak Reference 대상 을 각자 의 줄 로 이동 합 니 다. 예 를 들 어 현재 이 Weak Reference 는 특정한 Weak HashMap 대상 에 속 하면 해당 하 는 Reference Queue 줄 에 들 어 갑 니 다 (이 줄 은 링크 구조 입 니 다).       
    구체 적 인 디 테 일 을 알 고 싶 으 면 openjdk 의 소스 코드 인 스 턴 스 RefKlass. cpp 를 깊이 파 헤 쳐 lock 부분 을 얻 습 니 다.
    void instanceRefKlass::acquire_pending_list_lock(BasicLock *pending_list_basic_lock) { 
       // we may enter this with pending exception set 
       PRESERVE_EXCEPTION_MARK;  // exceptions are never thrown, needed for TRAPS argument 
       Handle h_lock(THREAD, java_lang_ref_Reference::pending_list_lock()); 
       ObjectSynchronizer::fast_enter(h_lock, pending_list_basic_lock, false, THREAD); 
       assert(ObjectSynchronizer::current_thread_holds_lock( 
                JavaThread::current(), h_lock), 
              "Locking should have succeeded"); 
       if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; 
     }

    GC 완료 후, pending 할당, lock 방출
    void instanceRefKlass::release_and_notify_pending_list_lock( 
       BasicLock *pending_list_basic_lock) { 
       // we may enter this with pending exception set 
       PRESERVE_EXCEPTION_MARK;  // exceptions are never thrown, needed for TRAPS argument 
       // 
       Handle h_lock(THREAD, java_lang_ref_Reference::pending_list_lock()); 
       assert(ObjectSynchronizer::current_thread_holds_lock( 
                JavaThread::current(), h_lock), 
              "Lock should be held"); 
       // Notify waiters on pending lists lock if there is any reference. 
       if (java_lang_ref_Reference::pending_list() != NULL) { 
         ObjectSynchronizer::notifyall(h_lock, THREAD); 
       } 
       ObjectSynchronizer::fast_exit(h_lock(), pending_list_basic_lock, THREAD); 
       if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; 
     }

    lock 석방 후, ReferenceHandler 스 레 드 가 정상 작 동 에 들 어가 면 pending 중 Reference 대상 이 각자 의 것 을 눌 렀 습 니 다. Reference Queue 중 
    private static class ReferenceHandler extends Thread {
         ReferenceHandler(ThreadGroup g, String name) {
             super(g, name);
         }
         public void run() {
             for (;;) {
             Reference r;
             synchronized (lock) {
                 if (pending != null) {
                 r = pending;
                 Reference rn = r.next;
                 pending = (rn == r) ? null : rn;
                 r.next = r;
                 } else {
                 try {
                     lock.wait();
                 } catch (InterruptedException x) { }
                 continue;
                 }
             }
             // Fast path for cleaners
             if (r instanceof Cleaner) {
                 ((Cleaner)r).clean();
                 continue;
             }
             ReferenceQueue q = r.queue;
             if (q != ReferenceQueue.NULL) q.enqueue(r);
             }
         }
     }

    위 부분 에 서 는 JVM 이 GC 에 있 을 때 Weak HashMap 의 key 메모 리 를 풀 어 주 었 다 고 했 습 니 다. 그러면... Weak HashMap 에서 Entry 데 이 터 를 어떻게 방출 하 는 지 보 세 요. Weak Hash Map 의 Reference Queue 는 어떻게 작용 합 니까? 
    Entry 는 어떻게 정리 합 니까?
    GC 이후 Weak HashMap 대상 에서 get, put 데이터 또는 size 방법 을 호출 할 때 Weak HashMap 은 HashMap 보다 하나 더 많 습 니 다. expungeStaleEntries () 방법
    private void expungeStaleEntries() {
         Entry<K,V> e;
             while ( (e = (Entry<K,V>) queue.poll()) != null) {
                 int h = e.hash;
                 int i = indexFor(h, table.length);
                 Entry<K,V> prev = table[i];
                 Entry<K,V> p = prev;
                 while (p != null) {
                     Entry<K,V> next = p.next;
                     if (p == e) {
                         if (prev == e)
                             table[i] = next;
                         else
                             prev.next = next;
                         e.next = null;  // Help GC
                         e.value = null; //  "   "
                         size--;
                         break;
                     }
                     prev = p;
                     p = next;
                 }
             }
         }

    expunge StaleEntries 방법 Reference Queue 열 에 있 는 WeakReference 를 poll 에 따라 나 와 Entry [] 데이터 와 비교 하 는 것 입 니 다. 같은 것 을 발견 하면 이 Entry 가 저장 한 대상 이 GC 에 의 해 제거 되 었 음 을 설명 합 니 다. 그러면 Entry [] 내의 Entry 대상 을 제거 하고 GC 에 의 해 떨 어 진 것 을 제거 합 니 다. Weak Reference 에 대응 하 는 Entry 는 Weak HashMap 에서 제거 되 었 습 니 다.  
    마지막.
    public static void main(String[] args) {
             Map<String, String> data = new WeakHashMap<String, String>();
             data.put("123", "123");
             data.put("124", "124");
             data.put("125", "125");
             data.put("126", "126");
             System.gc();
             data.size();
             System.out.println(data);
         }

    코드 에서 System. gc () 를 호출 한 후 data 의 데이터 가 비 워 졌 습 니까?정 답 은 없어 요.
    테스트 를 통 해 gc 가 모든 weakreference 대상 을 완전히 회수 할 수 있 는 것 은 아 닙 니 다. weakreference 도 old 구역 에 나타 날 수 있 습 니 다. 왜 GC 의 전략 을 봐 야 합 니까?

    좋은 웹페이지 즐겨찾기