1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.volley.toolbox; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.Comparator; 22 import java.util.LinkedList; 23 import java.util.List; 24 25 /** 26 * ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to 27 * supply those buffers to consumers who need to use them for a short period of time and then 28 * dispose of them. Simply creating and disposing such buffers in the conventional manner can 29 * considerable heap churn and garbage collection delays on Android, which lacks good management of 30 * short-lived heap objects. It may be advantageous to trade off some memory in the form of a 31 * permanently allocated pool of buffers in order to gain heap performance improvements; that is 32 * what this class does. 33 * <p> 34 * A good candidate user for this class is something like an I/O system that uses large temporary 35 * <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants 36 * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks 37 * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into 38 * account and also to maximize the odds of being able to reuse a recycled buffer, this class is 39 * free to return buffers larger than the requested size. The caller needs to be able to gracefully 40 * deal with getting buffers any size over the minimum. 41 * <p> 42 * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this 43 * class will allocate a new buffer and return it. 44 * <p> 45 * This class has no special ownership of buffers it creates; the caller is free to take a buffer 46 * it receives from this pool, use it permanently, and never return it to the pool; additionally, 47 * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there 48 * are no other lingering references to it. 49 * <p> 50 * This class ensures that the total size of the buffers in its recycling pool never exceeds a 51 * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit, 52 * least-recently-used buffers are disposed. 53 */ 54 public class ByteArrayPool { 55 /** The buffer pool, arranged both by last use and by buffer size */ 56 private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>(); 57 private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64); 58 59 /** The total size of the buffers in the pool */ 60 private int mCurrentSize = 0; 61 62 /** 63 * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay 64 * under this limit. 65 */ 66 private final int mSizeLimit; 67 68 /** Compares buffers by size */ 69 protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() { 70 @Override 71 public int compare(byte[] lhs, byte[] rhs) { 72 return lhs.length - rhs.length; 73 } 74 }; 75 76 /** 77 * @param sizeLimit the maximum size of the pool, in bytes 78 */ ByteArrayPool(int sizeLimit)79 public ByteArrayPool(int sizeLimit) { 80 mSizeLimit = sizeLimit; 81 } 82 83 /** 84 * Returns a buffer from the pool if one is available in the requested size, or allocates a new 85 * one if a pooled one is not available. 86 * 87 * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be 88 * larger. 89 * @return a byte[] buffer is always returned. 90 */ getBuf(int len)91 public synchronized byte[] getBuf(int len) { 92 for (int i = 0; i < mBuffersBySize.size(); i++) { 93 byte[] buf = mBuffersBySize.get(i); 94 if (buf.length >= len) { 95 mCurrentSize -= buf.length; 96 mBuffersBySize.remove(i); 97 mBuffersByLastUse.remove(buf); 98 return buf; 99 } 100 } 101 return new byte[len]; 102 } 103 104 /** 105 * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted 106 * size. 107 * 108 * @param buf the buffer to return to the pool. 109 */ returnBuf(byte[] buf)110 public synchronized void returnBuf(byte[] buf) { 111 if (buf == null || buf.length > mSizeLimit) { 112 return; 113 } 114 mBuffersByLastUse.add(buf); 115 int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR); 116 if (pos < 0) { 117 pos = -pos - 1; 118 } 119 mBuffersBySize.add(pos, buf); 120 mCurrentSize += buf.length; 121 trim(); 122 } 123 124 /** 125 * Removes buffers from the pool until it is under its size limit. 126 */ trim()127 private synchronized void trim() { 128 while (mCurrentSize > mSizeLimit) { 129 byte[] buf = mBuffersByLastUse.remove(0); 130 mBuffersBySize.remove(buf); 131 mCurrentSize -= buf.length; 132 } 133 } 134 135 } 136