`
GQM
  • 浏览: 24307 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

DirectByteBuffer vs. HeapByteBuffer选择问题

    博客分类:
  • java
阅读更多
引言:
最近基本完成了一个网络传输的framework,其中socket channel对于ByteBuffer的操作中遇到了HeapByteBuffer与DirectByteBuffer的选择问题,在这里做一下总结。

语法:
分配HeapByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(int capacity);

分配DirectByteBuffer
ByteBuffer buffer = ByteBuffer.allocateDirect(int capacity);


JDK的说明:
引用
A byte buffer is either direct or non-direct. Given a direct byte buffer, the Java virtual machine will make a best effort to perform native I/O operations directly upon it. That is, it will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.
A direct byte buffer may be created by invoking the allocateDirect factory method of this class. The buffers returned by this method typically have somewhat higher allocation and deallocation costs than non-direct buffers. The contents of direct buffers may reside outside of the normal garbage-collected heap, and so their impact upon the memory footprint of an application might not be obvious. It is therefore recommended that direct buffers be allocated primarily for large, long-lived buffers that are subject to the underlying system's native I/O operations. In general it is best to allocate direct buffers only when they yield a measureable gain in program performance.
A direct byte buffer may also be created by mapping a region of a file directly into memory. An implementation of the Java platform may optionally support the creation of direct byte buffers from native code via JNI. If an instance of one of these kinds of buffers refers to an inaccessible region of memory then an attempt to access that region will not change the buffer's content and will cause an unspecified exception to be thrown either at the time of the access or at some later time.


从文中大致可以看到DirectByteBuffer的特点如下:
  • 对于native IO operation,JVM会有最佳的性能效果(它不需要一个中间缓冲区,而是可以直接使用,避免了将buffer中的数据再复制到中间缓冲区)。
  • 由于DirectByteBuffer分配与native memory中,不在heap区,不会受到heap区的gc影响。(一般在old gen的full gc才会收集。)
  • 分配和释放需要更多的成本。

从上可以总结DirectByteBuffer大致的应用场景如下(socket通信和大文件处理还是比较适用的):
  • 频繁的native IO操作。
  • 系统的要求处理响应速度快和稳定,即高吞吐和低延迟。
  • ByteBuffer的生命周期长且容量需求较大,会占用较多的内存空间。

使用DirectByteBuffer的要点:
  • 尽量池化管理。
  • 作为HeapByteBuffer的一个备选。

应用中buffer选择问题的设计
基本思路为使用策略模式封装ByteBuffer的选择。
BufferHolder接口定义了ByteBuffer持有者的特性,包括持有和释放
/**
 * 接口定义<tt>ByteBuffer</tt>的持有者,
 * 
 * @author GQM
 * 
 */
public interface BufferHolder {

	/**
	 * 持有<tt>ByteBuffer</tt>对象。
	 * 
	 * @param buffer
	 */
	void hold(ByteBuffer buffer);

	
	/**
	 * 释放<tt>ByteBuffer</tt>对象。
	 * 
	 */
	void free();
}

BufferAllocator接口定义ByteBuffer的分配器。
/**
 * 接口定义了<tt>ByteBuffer</tt>分配器。分配器对 <tt>BufferHolder</tt>提供<tt>ByteBuffer</tt>
 * 的分配和释放功能。
 * 
 * @author GQM
 * 
 */
public interface BufferAllocator {

	/**
	 * <tt>BufferHolder</tt>申请持有分配器的分配的<tt>ByteBuffer</tt>对象。
	 * <tt>BufferEntry</tt>对象使用默认的容量。
	 * 
	 * @param holder
	 */
	void allocate(BufferHolder holder);

	/**
	 * <tt>BufferHolder</tt>申请持有分配器的分配的<tt>ByteBuffer</tt>对象。
	 * <tt>BufferEntry</tt>对象使用指定的容量。
	 * 
	 * @param holder
	 * @param bufferSize
	 */
	void allocate(BufferHolder holder, int bufferSize);

	/**
	 * <tt>BufferHolder</tt>申请释放其持有的<tt>ByteBuffer</tt>对象.
	 * 
	 * @param holder
	 */
	void release(BufferHolder holder);
}

FixedDirectBufferPool定义了DirectByteBuffer池的一种实现。包括两个结构,一个是当前可分配的DirectByteBuffer队列,一个是当前注册在用的DirectByteBuffer。
/**
 * 类实现了固定数量<tt>poolSize</tt>的direct的 <tt>ByteBuffer</tt>的池。池中的
 * <tt>ByteBuffer</tt>都是相同的<tt>capability</tt>,并管理 <tt>BufferHolder</tt>对
 * <tt>ByteBuffer</tt>的持有和释放。对无法满足的<tt>BufferHolder</tt>
 * 分配要求使用非池中的对象,采用no-direct的<tt>ByteBuffer</tt>。
 * 
 * @author GQM
 * 
 */
public class FixedDirectBufferPool implements BufferAllocator {

	protected final static Logger LOG = LoggerFactory
			.getLogger(FixedDirectBufferPool.class);

	private final LinkedList<ByteBuffer> readyQueue;
	private final HashMap<BufferHolder, ByteBuffer> dirtyQueue;
	private final int poolSize;
	private final int bufferSize;

	public FixedDirectBufferPool(int bufferSize, int poolSize) {
		this.poolSize = poolSize;
		this.bufferSize = bufferSize;
		this.readyQueue = new LinkedList<>();
		this.dirtyQueue = new HashMap<>();
		for (int i = 0; i < poolSize; i++) {
			this.readyQueue.add(ByteBuffer.allocateDirect(bufferSize));
		}
		LOG.info("Initialize the Direct Buffer Pool, size:{}",
				this.readyQueue.size());
	}

	@Override
	public void allocate(BufferHolder holder) {
		allocate(holder, bufferSize);
	}

	@Override
	public synchronized void allocate(BufferHolder holder, int bufferSize) {

		if (bufferSize <= this.bufferSize && !this.readyQueue.isEmpty()) {
			if (this.dirtyQueue.containsKey(holder)) {
				LOG.info(
						"the holder({}) has already hold a buffer, so do nothing",
						holder);
				return;
			}

			// poll an element
			ByteBuffer buffer = readyQueue.poll();
			// hold the buffer
			holder.hold(buffer);
			// register the holder
			this.dirtyQueue.put(holder, buffer);

			if (LOG.isDebugEnabled()) {
				LOG.debug(
						"[ALLOCATE SUCCESS] - holder:{}, pool:[ready:{}, dirty:{}]",
						holder, readyQueue.size(), dirtyQueue.size());
			}
		} else {
			LOG.warn(
					"[ALLOCATE FAILED] - ready:{}, dirty:{}, need bufferSize:{}",
					readyQueue.size(), dirtyQueue.size(), bufferSize);
			holder.hold((ByteBuffer.allocate(bufferSize)));
			if (LOG.isDebugEnabled()) {
				LOG.debug("allocate no-direct buffer, holder:{}", holder);
			}
		}
	}

	@Override
	public synchronized void release(BufferHolder holder) {
		if (holder != null && dirtyQueue.containsKey(holder)) {
			ByteBuffer buffer = this.dirtyQueue.remove(holder);
			this.readyQueue.add(buffer);
			buffer.clear();

			if (LOG.isDebugEnabled()) {
				LOG.debug(
						"[RELEASE SUCCESS] - holder:{}, pool:[ready:{}, dirty:{}]",
						holder, readyQueue.size(), dirtyQueue.size());
			}
		}
		holder.free();
	}

	public int getPoolSize() {
		return poolSize;
	}
}

HeapBufferAllocator定义了默认的HeapByteBuffer的分配方式
/**
 * 类实现了no-direct的<tt>ByteBuffer</tt>的分配和释放。
 * 
 * @author GQM
 * 
 */
public class HeapBufferAllocator implements BufferAllocator {

	private static final HeapBufferAllocator instance = new HeapBufferAllocator();
	protected static final int DEFAULT_BUFFER_SIZE = 64*1024;

	private HeapBufferAllocator() {
	}

	public static final HeapBufferAllocator getDefault() {
		return instance;
	}

	@Override
	public void allocate(BufferHolder holder) {
		allocate(holder, DEFAULT_BUFFER_SIZE);
	}

	@Override
	public void allocate(BufferHolder holder, int bufferSize) {
		ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
		holder.hold(buffer);
	}

	@Override
	public void release(BufferHolder holder) {
		holder.free();
	}

}

通过Strategy的设计模式封装了ByteBuffer的选择。


扩展:
JVM的内存组成
方法栈/本地方法栈线程创建时产生,方法执行时生成栈帧
方法区存储类的元数据信息、常量等
heapjava代码中所有的new操作
native memoryDirectByteBuffer、JNI、Compile、GC

DirectByteBuffer的回收:
http://iamzhongyong.iteye.com/blog/1743718
http://www.oschina.net/code/snippet_95947_3450
  • 大小: 34.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics