Android 캐 시 메커니즘-LruCache 의 상세 한 설명

개술
LruCache 의 핵심 원 리 는 바로 LinkedHashMap 에 대한 효과 적 인 이용 이다.그 내부 에 LinkedHashMap 구성원 변수 가 존재 하 는데 주의해 야 할 4 가지 방법 은 구조 방법,get,put,trimToSize 이다.
LRU(Least Recently Used)캐 시 알고리즘 이 생 겨 났 습 니 다.LRU 는 최근 에 가장 적 게 사용 하 는 알고리즘 입 니 다.캐 시가 가득 찼 을 때 최근 에 가장 적 게 사용 하 는 캐 시 대상 을 우선 도태 시 키 는 것 이 핵심 입 니 다.LRU 알고리즘 을 사용 한 캐 시 는 두 가지 가 있 습 니 다.LRhcache 와 DisLRuCache 는 각각 메모리 캐 시 와 하 드 디스크 캐 시 를 실현 하 는 데 사 용 됩 니 다.그 핵심 사상 은 모두 LRU 캐 시 알고리즘 입 니 다.
LRU 원리
LruCache 의 핵심 사상 은 캐 시 대상 목록 을 유지 하 는 것 입 니 다.그 중에서 대상 목록 의 배열 방식 은 방문 순서에 따라 이 루어 집 니 다.즉,방문 하지 않 은 대상 은 팀 머리 에 두 고 곧 도 태 될 것 입 니 다.최근 방문 대상 은 팀 뒤에 두 고 탈락 한다.파티 끝 에 요 소 를 추가 하고,파티 헤드 에서 요 소 를 삭제 합 니 다)
LruCache 는 사실 링크 드 HashMap 양 방향 링크 구 조 를 사 용 했 으 며,현재 링크 드 HashMap 의 사용 방법 을 분석 하고 있다.
1.구조 방법:

public LinkedHashMap(int initialCapacity,
 float loadFactor,
 boolean accessOrder) {
 super(initialCapacity, loadFactor);
 this.accessOrder = accessOrder;
}
accessOrder 가 true 일 때 이 집합 요소 의 순 서 는 방문 순서 입 니 다.즉,방문 한 후에 이 요 소 를 집합 맨 뒤에 두 는 것 입 니 다.
예 를 들 면:

LinkedHashMap < Integer, Integer > map = new LinkedHashMap < > (0, 0.75f, true);
map.put(0, 0);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.get(1);
map.get(2);

for (Map.Entry < Integer, Integer > entry: map.entrySet()) {
 System.out.println(entry.getKey() + ":" + entry.getValue());

}
출력 결과:
0:0
3:3
1:1
2:2
다음은 LruCache 소스 코드 에서 LinkedHashMap 을 어떻게 사용 하여 캐 시 추가,획득,삭 제 를 실현 하 는 지 구체 적 으로 살 펴 보 겠 습 니 다.

/**
  * @param maxSize for caches that do not override {@link #sizeOf}, this is
  *  the maximum number of entries in the cache. For all other caches,
  *  this is the maximum sum of the sizes of the entries in this cache.
  */
 public LruCache(int maxSize) {
  if (maxSize <= 0) {
   throw new IllegalArgumentException("maxSize <= 0");
  }
  this.maxSize = maxSize;
  this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//accessOrder    true
 }
LruCache 의 구조 함수 에서 바로 LinkedHashMap 의 접근 순 서 를 사용 한 것 을 볼 수 있 습 니 다.
2.puut()방법

/**
  * Caches {@code value} for {@code key}. The value is moved to the head of
  * the queue.
  *
  * @return the previous value mapped by {@code key}.
  */
 public final V put(K key, V value) {
  if (key == null || value == null) {//  ,    
   throw new NullPointerException("key == null || value == null");
  }

  V previous;
  synchronized (this) {
   putCount++;//       1
   size += safeSizeOf(key, value);//         
   previous = map.put(key, value);// map       
   if (previous != null) {//        ,          
    size -= safeSizeOf(key, previous);
   }
  }

  if (previous != null) {//entryRemoved()     ,      
   entryRemoved(false, key, previous, value);
  }

  trimToSize(maxSize);//      (    )
  return previous;
 }
put()방법 을 볼 수 있 습 니 다.중요 한 것 은 캐 시 대상 을 추가 한 후에 trimToSize()방법 으로 메모리 가 max Size 를 초과 하지 않도록 하 는 것 입 니 다.
3.trimToSize 방법
trimToSize()방법 다시 보기:

/**
  * Remove the eldest entries until the total of remaining entries is at or
  * below the requested size.
  *
  * @param maxSize the maximum size of the cache before returning. May be -1
  *   to evict even 0-sized elements.
  */
 public void trimToSize(int maxSize) {
  while (true) {//   
   K key;
   V value;
   synchronized (this) {
         //  map      size   0    size  0,    
    if (size < 0 || (map.isEmpty() && size != 0)) {
     throw new IllegalStateException(getClass().getName()
       + ".sizeOf() is reporting inconsistent results!");
    }
          //      size      ,  map  ,          ,    
    if (size <= maxSize) {
     break;
    }
          //    map       
    Map.Entry<K, V> toEvict = map.eldest();
    if (toEvict == null) {
     break;
    }

    key = toEvict.getKey();
    value = toEvict.getValue();
    map.remove(key);
    size -= safeSizeOf(key, value);
    evictionCount++;
   }

   entryRemoved(true, key, value, null);
  }
 }
trimToSize()방법 은 캐 시 크기 가 최대 치 보다 작 을 때 까지 링크 드 HashMap 의 헤더 요 소 를 계속 삭제 합 니 다.
4.get 방법
LruCache 의 get()방법 을 호출 하여 집합 에 있 는 캐 시 대상 을 가 져 올 때 이 요 소 를 한 번 방문 한 것 을 의미 합 니 다.대기 열 을 업데이트 하고 전체 대기 열 을 방문 순서에 따라 정렬 합 니 다.이 업데이트 과정 은 링크 드 HashMap 의 get()방법 에서 이 루어 졌 습 니 다.
이어서 LuCache 의 get()방법 을 보도 록 하 겠 습 니 다.

/**
  * Returns the value for {@code key} if it exists in the cache or can be
  * created by {@code #create}. If a value was returned, it is moved to the
  * head of the queue. This returns null if a value is not cached and cannot
  * be created.
  */
 public final V get(K key) {
  if (key == null) {//key    
   throw new NullPointerException("key == null");
  }

  V mapValue;
  synchronized (this) {
        /          
   mapValue = map.get(key);
   if (mapValue != null) {
    hitCount++;
    return mapValue;
   }
   missCount++;
  }
LruCache 를 보 는 get 방법 은 실제 LinkedHashMap 의 get 방법 을 호출 한 것 입 니 다.

public V get(Object key) {
  LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
  if (e == null)
   return null;
  e.recordAccess(this);//       
  return e.value;
 }
그리고 링크 드 Hash MapEntry 의 recordAccess 방법 을 살 펴 보 겠 습 니 다.

/**
   * This method is invoked by the superclass whenever the value
   * of a pre-existing entry is read by Map.get or modified by Map.set.
   * If the enclosing Map is access-ordered, it moves the entry
   * to the end of the list; otherwise, it does nothing.
   */
  void recordAccess(HashMap<K,V> m) {
   LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
   if (lm.accessOrder) {//         
    lm.modCount++;
    remove();//     
    addBefore(lm.header);//        
   }
  }
recordAccess 방법의 역할 은 accessOrder 가 true 라면 존재 하 는 entry 를 get 에서 읽 거나 set 편집 을 한 후에 팀 끝으로 옮 기 는 것 입 니 다.그렇지 않 으 면 어떠한 조작 도 하지 않 습 니 다.
즉,이 방법의 작용 은 방금 방문 한 원 소 를 집합의 마지막 자리 에 놓 는 것 이다
요약:
루 캐 시의 핵심 원 리 는 링크 드 하 쉬 맵 대상 을 효과적으로 활용 하 는 것 이다.구조 방법 에서 max Size 를 설정 하고 accessOrder 를 true 로 설정 합 니 다.get 을 실행 하면 방문 요 소 를 대기 열 끝 에 두 고 put 작업 을 한 후에 trimToSize 를 호출 하여 LinkedHashMap 의 크기 가 max Size 보다 크 지 않 습 니 다.
위 에서 말 한 것 은 편집장 이 여러분 에 게 소개 한 안 드 로 이 드 캐 시 메커니즘 인 LruCache 의 상세 한 통합 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.편집장 은 신속하게 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기