LruCacheの原理

8276 ワード

LruCacheの中で最も重要な要素はいくつかあります
  • LruCacheキャッシュのサイズを設定します.通常、現在のプロセスで使用可能な容量の1/8
  • です.
  • sizeOfメソッドを書き換える必要があり、キャッシュする各ピクチャのサイズ
  • を算出する.
    int maxMemory = (int) (Runtime.getRuntime().totalMemory()/1024);
            int cacheSize = maxMemory/8;
            mMemoryCache = new LruCache(cacheSize){
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getRowBytes()*value.getHeight()/1024;
                }
            };
    
    

    ここで、sizeOf()メソッドを書き直さないと、デフォルトでメモリsizeを計算する方法に注意してください.ソースコードを見てみましょう.
    	/**
         * Returns the size of the entry for {@code key} and {@code value} in
         * user-defined units.  The default implementation returns 1 so that size
         * is the number of entries and max size is the maximum number of entries.
         *
         * 

    An entry's size must not change while it is in the cache. */ protected int sizeOf(K key, V value) { return 1; }


    ソースコードでsizeOfの戻り値を1に書き換えないと、デフォルトでitemごとに占めるメモリのサイズが1になりますLruCacheクラスの一番上にこんな言葉があります
    * 

    By default, the cache size is measured in the number of entries. Override * {@link #sizeOf} to size the cache in different units. For example, this cache * is limited to 4MiB of bitmaps: *

       {@code
     *   int cacheSize = 4 * 1024 * 1024; // 4MiB
     *   LruCache bitmapCache = new LruCache(cacheSize) {
     *       protected int sizeOf(String key, Bitmap value) {
     *           return value.getByteCount();
     *       }
     *   }}
    デフォルトでは、キャッシュサイズはエントリの で されます.sizeOfメソッドを き えて、 なるセルのキャッシュサイズを します.たとえば、このキャッシュbitmapsは4 MiB:sizeOfの き えに されます.
     int cacheSize = 4 * 1024 * 1024; // 4MiB
     LruCache bitmapCache = new LruCache(cacheSize) {
          protected int sizeOf(String key, Bitmap value) {
             return value.getByteCount();
           }
      }
    

    なぜかというとデフォルトでは、キャッシュ・サイズはエントリの で されます.
    メモリサイズの を ればわかります
    public void trimToSize(int maxSize) {
            while (true) {
                K key;
                V value;
                synchronized (this) {
                    if (size < 0 || (map.isEmpty() && size != 0)) {
                        throw new IllegalStateException(getClass().getName()
                                + ".sizeOf() is reporting inconsistent results!");
                    }
    
                    if (size <= maxSize) {
                        break;
                    }
    
                    Map.Entry 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);
            }
        }
    

    sizeはメモリのリアルタイムサイズで、trimToSizeのsize-=safeSizeOf(key,value);リアルタイムサイズの
    safeSizeOfの を てみましょう
    private int safeSizeOf(K key, V value) {
            int result = sizeOf(key, value);
            if (result < 0) {
                throw new IllegalStateException("Negative size: " + key + "=" + value);
            }
            return result;
        }
    

    び されたのはsizeOfで されたサイズsizeOfを き えていない に されるサイズが1です.たとえば、LruCacheのcacheSizeは1024に されています.つまり、maxSize=1024はsizeを するときにitemごとのサイズを し、itemごとのサイズはデフォルトの1に されています.この 、 を しているように えます
  • アルゴリズム
  • の アルゴリズムの はLinkedHashMapによって され,ソフトリファレンスはなく,いずれも いリファレンス
  • である.
  • されたデータが された より きい は、 にキャッシュされたデータを してメモリを します. の な はtrimToSize における
  • である.
    LinkedHashMap
  • LinkedHashMapはHashMapに され、 チェーンテーブルを してMapのEntry を します.
  • このような には、LRU と の2 があり、 によって
  • を することができる.
  • LRU (アクセス : チェーンテーブルの がアクセスの に されていることを す):get にそのオブジェクトをチェーンテーブルの に し、put の しいオブジェクトもチェーンテーブルの に され、メモリキャッシュが の に したときにチェーンテーブルヘッダのオブジェクト( も ない)を し、
  • .
  • : の に って され、put に する しいオブジェクトはチェーンテーブルの に され、すなわちフラグビットaccessOrderの がfalseの
  • である.
    LinkedHashMapの の
    による
    public LinkedHashMap(int initialCapacity,
                             float loadFactor,
                             boolean accessOrder) {
            super(initialCapacity, loadFactor);
            this.accessOrder = accessOrder;
        }
    

    ここでaccessOrderがtrueに されている はアクセス 、falseに されている は
    :trueに すると
    public static final void main(String[] args) {
            LinkedHashMap map = new LinkedHashMap<>(0, 0.75f, true);
            map.put(0, 0);
            map.put(1, 1);
            map.put(2, 2);
            map.put(3, 3);
            map.put(4, 4);
            map.put(5, 5);
            map.put(6, 6);
            map.get(1);
            map.get(2);
    
            for (Map.Entry entry : map.entrySet()) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
    
            }
        }
    

    :
    0:0
    3:3
    4:4
    5:5
    6:6
    1:1
    2:2
    

    これは、 もアルゴリズムを している (アクセス )です.
    LruCacheはキャッシュとして いられているが,キャッシュに わると ずput,getメソッドがある , にLruCacheにおけるputとgetメソッドを る.
    LruCacheでのputメソッド:
    public final V put(K key, V value) {
             //    ,      
            if (key == null || value == null) {
                throw new NullPointerException("key == null || value == null");
            }
            V previous;
            synchronized (this) {
                //         1
                putCount++;
                //         
                size += safeSizeOf(key, value);
               // map       
                previous = map.put(key, value);
                //        ,          
                if (previous != null) {
                    size -= safeSizeOf(key, previous);
                }
            }
            //entryRemoved()     ,      
            if (previous != null) {
                entryRemoved(false, key, previous, value);
            }
            //      (    )
            trimToSize(maxSize);
            return previous;
        }
    

    キーはputメソッドが び されたときにtrimToSize()メソッドも され、キャッシュサイズが かどうかを し、 している は アクセスの ない を する があります.
    trimToSize()メソッドを て
     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      ,          ,    
                    if (size <= maxSize) {
                        break;
                    }
                    //  head,         
                    Map.Entry 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);
            }
        }
    

    LruCacheでのget()メソッド
    public final V get(K key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
    
            V mapValue;
            synchronized (this) {
                //         
                //LinkedHashMap   get()                     
                mapValue = map.get(key);
                mapValue = map.get(key);
                if (mapValue != null) {
                    hitCount++;
                    return mapValue;
                }
                missCount++;
            }
            //  
        }
    

    LinkedHashMapへのget()メソッド
    public V get(Object key) {
            Node e;
            if ((e = getNode(hash(key), key)) == null)
                return null;
            if (accessOrder)//       
                //             
                afterNodeAccess(e);
            return e.value;
        }
    

    LinkedHashMapの をもっと しく ることができます