1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.io;
19
20 import java.nio.ByteBuffer;
21 import java.util.Queue;
22 import java.util.concurrent.atomic.AtomicLong;
23 import java.util.concurrent.locks.ReentrantLock;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.hbase.util.BoundedArrayQueue;
29
30 import com.google.common.annotations.VisibleForTesting;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @InterfaceAudience.Private
49 public class BoundedByteBufferPool {
50 private static final Log LOG = LogFactory.getLog(BoundedByteBufferPool.class);
51
52 @VisibleForTesting
53 final Queue<ByteBuffer> buffers;
54
55
56 private final int maxByteBufferSizeToCache;
57
58
59 @VisibleForTesting
60 volatile int runningAverage;
61
62
63 private volatile int totalReservoirCapacity;
64
65
66 private AtomicLong allocations = new AtomicLong(0);
67
68 private ReentrantLock lock = new ReentrantLock();
69
70 private boolean createDirectByteBuffer;
71
72
73
74
75
76
77
78 public BoundedByteBufferPool(final int maxByteBufferSizeToCache, final int initialByteBufferSize,
79 final int maxToCache, final boolean createDirectByteBuffer) {
80 this.maxByteBufferSizeToCache = maxByteBufferSizeToCache;
81 this.runningAverage = initialByteBufferSize;
82 this.buffers = new BoundedArrayQueue<ByteBuffer>(maxToCache);
83 this.createDirectByteBuffer = createDirectByteBuffer;
84 }
85
86 public ByteBuffer getBuffer() {
87 ByteBuffer bb = null;
88 lock.lock();
89 try {
90 bb = this.buffers.poll();
91 if (bb != null) {
92 this.totalReservoirCapacity -= bb.capacity();
93 }
94 } finally {
95 lock.unlock();
96 }
97 if (bb != null) {
98
99 bb.clear();
100 } else {
101 bb = this.createDirectByteBuffer ? ByteBuffer.allocateDirect(this.runningAverage)
102 : ByteBuffer.allocate(this.runningAverage);
103 this.allocations.incrementAndGet();
104 }
105 if (LOG.isTraceEnabled()) {
106 LOG.trace("runningAverage=" + this.runningAverage +
107 ", totalCapacity=" + this.totalReservoirCapacity + ", count=" + this.buffers.size() +
108 ", allocations=" + this.allocations.get());
109 }
110 return bb;
111 }
112
113 public void putBuffer(ByteBuffer bb) {
114
115 if (bb.capacity() > this.maxByteBufferSizeToCache) return;
116 boolean success = false;
117 int average = 0;
118 lock.lock();
119 try {
120 success = this.buffers.offer(bb);
121 if (success) {
122 this.totalReservoirCapacity += bb.capacity();
123 average = this.totalReservoirCapacity / this.buffers.size();
124 }
125 } finally {
126 lock.unlock();
127 }
128 if (!success) {
129 if (LOG.isDebugEnabled()) {
130 LOG.debug("At capacity: " + this.buffers.size());
131 }
132 } else {
133 if (average > this.runningAverage && average < this.maxByteBufferSizeToCache) {
134 this.runningAverage = average;
135 }
136 }
137 }
138 }