Volley中的PoolingByteArrayOutputStream

Volley中的PoolingByteArrayOutputStream

在启动Dalvik虚拟机的时候,我们可以分别通过-Xms-Xmx-XX:HeapGrowthLimit三个选项来指定上述三个值,以上三个值分别表示表示:

Starting Size: Dalvik虚拟机启动的时候,会先分配一块初始的堆内存给虚拟机使用。

Growth Limit: 是系统给每一个程序的最大堆上限,超过这个上限,程序就会OOM。

Maximum Size: 不受控情况下的最大堆内存大小,起始就是我们在用largeheap属性的时候,可以从系统获取的最大堆大小。

同时除了上面的这个三个指标外,还有几个指标也是值得我们关注的,那就是堆最小空闲值
(Min Free)、堆最大空闲值(Max Free)和堆目标利用率(Target Utilization)。
假设在某一次GC之后,存活对象占用内存的大小为LiveSize,那么这时候堆的理想大小应该
为(LiveSize / U)。但是(LiveSize / U)必须大于等于(LiveSize + MinFree)并且
小于等于(LiveSize + MaxFree),每次GC后垃圾回收器都会尽量让堆的利用率往目标利用
率靠拢。所以当我们尝试手动去生成一些几百K的对象,试图去扩大可用堆大小的时候,反而
会导致频繁的GC,因为这些对象的分配会导致GC,而GC后会让堆内存回到合适的比例,而我
们使用的局部变量很快会被回收理论上存活对象还是那么多,我们的堆大小也会缩减回来无法
达到扩充的目的。 与此同时这也是产生CONCURRENT GC的一个因素,因为手动生成的对象可
能会使堆内存的使用达到触发CONCURRENT GC的阈值。

上述由堆目标利用率导致的堆容量重置可用PoolingByteArrayOutputStream来避免,ByteArrayPool维护的byte数组缓存池减少内存分配的操作,自然也就减少GC的发生。
PoolingByteArrayOutputStream借助一个ByteArrayPool类来维护byte数组缓存池,因为频繁创建、清除byte数组可能引起严重的内存抖动。
下面是分析,精华都在注释里!

ByteArrayPool主要代码:
public class ByteArrayPool {
    //全部byte数组的占用空间限制
    private final int mSizeLimit;

    //mBuffersByLastUse维护缓存byte数组的使用顺序,在压缩缓存池的时候将删掉最后的byte数组
    private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();

    //mBuffersBySize维护缓存byte数组的大小顺序,PoolingByteArrayOutputStream按需提取byte数组
    private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);

    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;
                mBuffersBySize.remove(i);
                mBuffersByLastUse.remove(buf);
                return buf;
            }
        }
        //可以返回大于mSizeLimit的byte数组,后续不会进入mBuffers
        return new byte[len];
    }

    /**
     * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
     * size.
     *
     * @param buf the buffer to return to the pool.
     */
    public synchronized void returnBuf(byte[] buf) {
        if (buf == null || buf.length > mSizeLimit) {
            return;
        }
        mBuffersByLastUse.add(buf);
        //二分查找
        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
        if (pos < 0) {
            pos = -pos - 1;
        }
        mBuffersBySize.add(pos, buf);
        mCurrentSize += buf.length;
        trim();
    }

    /**
     * Removes buffers from the pool until it is under its size limit.
     */
    private synchronized void trim() {
        while (mCurrentSize > mSizeLimit) {
            byte[] buf = mBuffersByLastUse.remove(0);
            mBuffersBySize.remove(buf);
            mCurrentSize -= buf.length;
        }
    }
PoolingByteArrayOutputStream主要代码:
public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {

   public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {
        mPool = pool;
        buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));
    }

    private void expand(int i) {
        /* Can the buffer handle @i more bytes, if not expand it */
        if (count + i <= buf.length) {
            return;
        }
        byte[] newbuf = mPool.getBuf((count + i) * 2);
        System.arraycopy(buf, 0, newbuf, 0, count);
        mPool.returnBuf(buf);
        buf = newbuf;
    }

    @Override
    public synchronized void write(byte[] buffer, int offset, int len) {
        //取代父类ByteArrayOutputStream的grow方法实现扩大缓存buf
        //(没有像grow方法那样针对OutOfMemory做处理,虽然一般不会出现)
        expand(len);
        super.write(buffer, offset, len);
    }

    @Override
    public synchronized void write(int oneByte) {
        expand(1);
        super.write(oneByte);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值