Volley之ByteArrayPool——LruCache实现

Volley的ByteArrayPool是一个byte[]缓存池,用于限制最大缓存量并采用LRU策略进行回收。当超过设定的最大值时,会删除最少最近使用的byte[],避免频繁创建导致堆内存消耗。其工作原理类似于一个有序集合,最近使用的byte[]位于最后,便于回收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础

        其主要作用是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
    }
    //剩余代码无意义,略
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值