All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.io.BoundedByteBufferPool Maven / Gradle / Ivy

The newest version!
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hbase.io;

import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.util.BoundedArrayQueue;

import com.google.common.annotations.VisibleForTesting;

/**
 * Like Hadoops' ByteBufferPool only you do not specify desired size when getting a ByteBuffer.
 * This pool keeps an upper bound on the count of ByteBuffers in the pool and on the maximum size
 * of ByteBuffer that it will retain (Hence the pool is 'bounded' as opposed to, say,
 * Hadoop's ElasticByteBuffferPool).
 * If a ByteBuffer is bigger than the configured threshold, we will just let the ByteBuffer go
 * rather than add it to the pool. If more ByteBuffers than the configured maximum instances,
 * we will not add the passed ByteBuffer to the pool; we will just drop it
 * (we will log a WARN in this case that we are at capacity).
 *
 * 

The intended use case is a reservoir of bytebuffers that an RPC can reuse; buffers tend to * achieve a particular 'run' size over time give or take a few extremes. Set TRACE level on this * class for a couple of seconds to get reporting on how it is running when deployed. * *

This class is thread safe. */ @InterfaceAudience.Private @SuppressWarnings("NonAtomicVolatileUpdate") // Suppress error-prone warning, see HBASE-21162 public class BoundedByteBufferPool { private static final Log LOG = LogFactory.getLog(BoundedByteBufferPool.class); @VisibleForTesting final Queue buffers; // Maximum size of a ByteBuffer to retain in pool private final int maxByteBufferSizeToCache; // A running average only it only rises, it never recedes @VisibleForTesting volatile int runningAverage; // Scratch that keeps rough total size of pooled bytebuffers private volatile int totalReservoirCapacity; // For reporting private AtomicLong allocations = new AtomicLong(0); private ReentrantLock lock = new ReentrantLock(); private boolean createDirectByteBuffer; /** * @param maxByteBufferSizeToCache * @param initialByteBufferSize * @param maxToCache * @param createDirectByteBuffer whether the buffers created by this pool to be off heap */ public BoundedByteBufferPool(final int maxByteBufferSizeToCache, final int initialByteBufferSize, final int maxToCache, final boolean createDirectByteBuffer) { this.maxByteBufferSizeToCache = maxByteBufferSizeToCache; this.runningAverage = initialByteBufferSize; this.buffers = new BoundedArrayQueue(maxToCache); this.createDirectByteBuffer = createDirectByteBuffer; } public ByteBuffer getBuffer() { ByteBuffer bb = null; lock.lock(); try { bb = this.buffers.poll(); if (bb != null) { this.totalReservoirCapacity -= bb.capacity(); } } finally { lock.unlock(); } if (bb != null) { // Clear sets limit == capacity. Postion == 0. bb.clear(); } else { bb = this.createDirectByteBuffer ? ByteBuffer.allocateDirect(this.runningAverage) : ByteBuffer.allocate(this.runningAverage); this.allocations.incrementAndGet(); } if (LOG.isTraceEnabled()) { LOG.trace("runningAverage=" + this.runningAverage + ", totalCapacity=" + this.totalReservoirCapacity + ", count=" + this.buffers.size() + ", allocations=" + this.allocations.get()); } return bb; } public void putBuffer(ByteBuffer bb) { // If buffer is larger than we want to keep around, just let it go. if (bb.capacity() > this.maxByteBufferSizeToCache) return; boolean success = false; int average = 0; lock.lock(); try { success = this.buffers.offer(bb); if (success) { this.totalReservoirCapacity += bb.capacity(); average = this.totalReservoirCapacity / this.buffers.size(); // size will never be 0. } } finally { lock.unlock(); } if (!success) { if (LOG.isDebugEnabled()) { LOG.debug("At capacity: " + this.buffers.size()); } } else { if (average > this.runningAverage && average < this.maxByteBufferSizeToCache) { this.runningAverage = average; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy