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 @SuppressWarnings("NonAtomicVolatileUpdate")
50 public class BoundedByteBufferPool {
51 private static final Log LOG = LogFactory.getLog(BoundedByteBufferPool.class);
52
53 @VisibleForTesting
54 final Queue<ByteBuffer> buffers;
55
56
57 private final int maxByteBufferSizeToCache;
58
59
60 @VisibleForTesting
61 volatile int runningAverage;
62
63
64 private volatile int totalReservoirCapacity;
65
66
67 private AtomicLong allocations = new AtomicLong(0);
68
69 private ReentrantLock lock = new ReentrantLock();
70
71 private boolean createDirectByteBuffer;
72
73
74
75
76
77
78
79 public BoundedByteBufferPool(final int maxByteBufferSizeToCache, final int initialByteBufferSize,
80 final int maxToCache, final boolean createDirectByteBuffer) {
81 this.maxByteBufferSizeToCache = maxByteBufferSizeToCache;
82 this.runningAverage = initialByteBufferSize;
83 this.buffers = new BoundedArrayQueue<ByteBuffer>(maxToCache);
84 this.createDirectByteBuffer = createDirectByteBuffer;
85 }
86
87 public ByteBuffer getBuffer() {
88 ByteBuffer bb = null;
89 lock.lock();
90 try {
91 bb = this.buffers.poll();
92 if (bb != null) {
93 this.totalReservoirCapacity -= bb.capacity();
94 }
95 } finally {
96 lock.unlock();
97 }
98 if (bb != null) {
99
100 bb.clear();
101 } else {
102 bb = this.createDirectByteBuffer ? ByteBuffer.allocateDirect(this.runningAverage)
103 : ByteBuffer.allocate(this.runningAverage);
104 this.allocations.incrementAndGet();
105 }
106 if (LOG.isTraceEnabled()) {
107 LOG.trace("runningAverage=" + this.runningAverage +
108 ", totalCapacity=" + this.totalReservoirCapacity + ", count=" + this.buffers.size() +
109 ", allocations=" + this.allocations.get());
110 }
111 return bb;
112 }
113
114 public void putBuffer(ByteBuffer bb) {
115
116 if (bb.capacity() > this.maxByteBufferSizeToCache) return;
117 boolean success = false;
118 int average = 0;
119 lock.lock();
120 try {
121 success = this.buffers.offer(bb);
122 if (success) {
123 this.totalReservoirCapacity += bb.capacity();
124 average = this.totalReservoirCapacity / this.buffers.size();
125 }
126 } finally {
127 lock.unlock();
128 }
129 if (!success) {
130 if (LOG.isDebugEnabled()) {
131 LOG.debug("At capacity: " + this.buffers.size());
132 }
133 } else {
134 if (average > this.runningAverage && average < this.maxByteBufferSizeToCache) {
135 this.runningAverage = average;
136 }
137 }
138 }
139 }