Volley之ByteArrayPool——LruCache实现

来源:互联网 发布:淘宝网店铺排名 编辑:程序博客网 时间:2024/05/29 15:32

基础

        其主要作用是byte[]的缓存池,可以指定最大的缓存的byte数目。当缓存的byte数目超过指定的最大值时,以LRU策略进行回收。在频繁进行I/O操作时,如果不停地创建byte[],会导致堆内存的极速消耗,因为gc的回收并不是太及时。

原理

        用一个有序集合存储缓存的byte[],用另一个集合存储待删的byte[]——在缓存池满了的时候删除该集合中的最前面元素,直到缓存池有空闲为止。

源码及注释

import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.LinkedList;import java.util.List;/** * byte[]的缓存池,可以指定最大的缓存的byte数目。当缓存的byte数目超过指定的最大值时,以LRU策略进行回收。 * 用一个集合记录该byte[]的使用顺序,使用另一个集合记录已经使用过的byte[],保证对byte[]的重复利用。 */public class ByteArrayPool {    /**     * byte[]待删集合,记录了byte[]的使用顺序。当缓存的byte数目超过指定的最大值时,会回收该list集合中的第一个元素。     * 每一次从变量mBuffersBySize中获取到合适的byte[]时,会将返回值从该集合中删除,因为这个byte[]是最近使用的。     * 每一次回收该集合中第0个元素,保证了回收的byte[]是使用时间最久远的。     */    private List<byte[]> mBuffersByLastUse = new LinkedList<>();    /**     * byte[]的真正缓存list集合。每一次获取时都是从该集合中获取或者新生成     */    private List<byte[]> mBuffersBySize = new ArrayList<>(64);    /**     * 当前缓存池中已经有byte数     */    private int mCurrentSize = 0;    /**     * 本缓存池中最大的缓存byte数     */    private final int mSizeLimit;    /**      * 哪个byte数组元素少,哪个在前     */    protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {        @Override        public int compare(byte[] lhs, byte[] rhs) {            return lhs.length - rhs.length;        }    };    public ByteArrayPool(int sizeLimit) {        mSizeLimit = sizeLimit;    }    /**     * @param len.返回的byte[]的最小长度,有可能返回的byte[]的长度大于该len     */    public synchronized byte[] getBuf(int len) {        for (int i = 0; i < mBuffersBySize.size(); i++) {            byte[] buf = mBuffersBySize.get(i);            if (buf.length >= len) {                mCurrentSize -= buf.length;//返回后,将当前缓存池中缓存的byte数目减少                mBuffersBySize.remove(i);//删掉,保证了一个byte[]数组不会提供给两个客户端使用                //本次使用了该缓存,所以将其从待删集合中删除,这样即使缓存的byte数量超过最大的范围,也不会被删掉。保证了最新的不会被删。                //同时,当一个byte[]被多次使用时,则该byte[]会被存储到该集合的最后端,也不会被立即回收                mBuffersByLastUse.remove(buf);                return buf;            }        }        return new byte[len];//没有合适的或者第一次会直接返回    }    /**     * 将使用过的byte数组返回到缓存池中     */    public synchronized void returnBuf(byte[] buf) {        if (buf == null || buf.length > mSizeLimit) {            return;        }        mBuffersByLastUse.add(buf);//待删集合,不能对回收的byte[]进行排序        //该二分查找的返回值有两个效果:其一知道查找的有木有,其二如果木有,插入时插入的位置。        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);        if (pos < 0) {            pos = -pos - 1;        }        mBuffersBySize.add(pos, buf);        mCurrentSize += buf.length;        trim();    }    /**     * 回收该回收的部分。     */    private synchronized void trim() {        while (mCurrentSize > mSizeLimit) {            byte[] buf = mBuffersByLastUse.remove(0);            mBuffersBySize.remove(buf);            mCurrentSize -= buf.length;        }    }}

        主要思路都在注释中。其实其主要思路就是:将最新使用的放在mBuffersByLastUse的最后面,这样回收的就最晚。而且LruCache的实现思路也和这个完全一样。

LruCache

import java.util.LinkedHashMap;import java.util.Map;public class LruCache<K, V> {    private final LinkedHashMap<K, V> map;//使用LinkedHashMap的主要原因在于:它的put的顺序与迭代器的取的顺序一致。    /** 最大的缓存数*/    private int size;    private int maxSize;    private int putCount;    private int createCount;    private int evictionCount;    private int hitCount;    private int missCount;    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);    }    /**     * 重新定义最大的缓存大小     */    public void resize(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        synchronized (this) {            this.maxSize = maxSize;        }        trimToSize(maxSize);    }    public final V get(K key) {        if (key == null) {            throw new NullPointerException("key == null");        }        V mapValue;        synchronized (this) {            mapValue = map.get(key);            if (mapValue != null) {                hitCount++;                return mapValue;            }            missCount++;        }        /*         * 通过key值获取value值。如果该key值对应的有value了,则放弃create()返回的值。 * 因为create()可能耗费的时间比较长,所以create()执行完毕后,key可能对应的有value。         */        V createdValue = create(key);        if (createdValue == null) {            return null;        }        synchronized (this) {            createCount++;            mapValue = map.put(key, createdValue);            if (mapValue != null) {                // key值有冲突,放弃create()生成的value                map.put(key, mapValue);            } else {                size += safeSizeOf(key, createdValue);            }        }        if (mapValue != null) {            entryRemoved(false, key, createdValue, mapValue);            return mapValue;        } else {            trimToSize(maxSize);            return createdValue;        }    }    /**      * 这里是先将数据加入缓存中,再判断缓存的大小是否超过了限制。     * 这会导致在临界状态下依旧会OOM,可以先移除多余的部分,再将新的加入到缓存中。     */    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++;            size += safeSizeOf(key, value);            previous = map.put(key, value);//返回key对应的value。            if (previous != null) {                size -= safeSizeOf(key, previous);//previous是要被删除的,所以size应减除previous的大小            }        }        if (previous != null) {            entryRemoved(false, key, previous, value);        }        trimToSize(maxSize);        return previous;    }    /**     * 移除存在时间最久的对象,直到缓存的大小不大于最大缓存范围     * 整理整个缓存池,防止超出最大的范围     */    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 || map.isEmpty()) {                    break;                }//因为获取的顺序与存储的顺序一致,所以该句得到的就是存在时间最久的缓存对象。                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();                key = toEvict.getKey();                value = toEvict.getValue();                map.remove(key);                size -= safeSizeOf(key, value);                evictionCount++;            }            entryRemoved(true, key, value, null);        }    }    public final V remove(K key) {        if (key == null) {            throw new NullPointerException("key == null");        }        V previous;        synchronized (this) {            previous = map.remove(key);            if (previous != null) {                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, null);        }        return previous;    }    /**     * oldValue从缓存池中移除时的回调     */    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}    /**     * 根据key值生成value。     */    protected V create(K key) {        return null;    }/** * 对sizeOf()的大小加一层判断而已 */    private int safeSizeOf(K key, V value) {        int result = sizeOf(key, value);        if (result < 0) {            throw new IllegalStateException("Negative size: " + key + "=" + value);        }        return result;    }    /**     * 获取对应value的大小。一般都需要重写该方法     */    protected int sizeOf(K key, V value) {        return 1;    }    /**     * trimToSize()传入-1,则所有的缓存对象都会被清除     */    public final void evictAll() {        trimToSize(-1); // -1 will evict 0-sized elements    }    //剩余代码无意义,略}






0 0