`
GQM
  • 浏览: 24873 次
  • 性别: 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
分享到:
评论

相关推荐

    【IT十八掌徐培成】Java基础第26天-07.DirectByteBuffer.zip

    与HeapByteBuffer不同,DirectByteBuffer的读写速度通常更快,因为它避免了Java对象之间的复制。 4. **性能优化**:在处理大量数据或高并发的网络传输时,使用DirectByteBuffer可以减少不必要的内存拷贝和提高效率...

    java8源码-netty-learn:这是一个用于netty学习的工程

    DirectByteBuffer HeapByteBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer CharBuffer Selector选择器 Selector的作用就是配合一个线程来管理多个channel,获取这些channel上发生

    Java-NIO-Programming-Cookbook(含源码)

    8. **效率比较**:在特定场景下,`DirectByteBuffer`可能比`HeapByteBuffer`更快,因为减少了数据在Java堆和物理内存之间的拷贝。但并非所有情况都适用,具体取决于系统配置和数据量。 通过这本书和提供的源码,你...

    Netty4.x源码分析详解

    1. **ByteBufAllocator**: 提供了多种内存分配策略,如 DirectByteBuffer 和 HeapByteBuffer,可根据场景选择最合适的内存分配方式。 2. **ChannelHandlerContext**: 作为 Handler 与 EventLoop 和 Pipeline 交互的...

    Netty面试专题.pdf

    - DirectByteBuffer和HeapByteBuffer是Buffer的两种类型。DirectByteBuffer是直接分配于系统内存的Buffer,减少了一次从内核空间到用户空间的复制,适用于大型、持久的缓冲区。HeapByteBuffer则是由JVM管理的Buffer...

    NIO trick and trap .pdf

    - **ByteBuffer的选择**:根据应用场景选择合适的`ByteBuffer`类型,如使用`DirectByteBuffer`来减少数据复制。 - **ViewByteBuffer**:提供视图,允许共享相同的底层缓冲区数据,提高效率。 - **FileChannel....

    Android中的ByteBuffer解析

    1. **选择适当的缓冲区类型**:根据应用场景选择HeapByteBuffer(堆内缓冲区)或DirectByteBuffer(堆外缓冲区)。 2. **预估大小**:合理预估缓冲区大小,避免频繁扩容导致的额外开销。 3. **复用缓冲区**:在多...

    【IT十八掌徐培成】Java基础第27天-02.NIO-ServerSocketChannel-SocketChannel.zip

    缓冲区有几种不同的实现,如HeapByteBuffer(基于堆内存)、DirectByteBuffer(直接内存)等,它们各有优缺点,根据具体需求选择合适的类型。 5. **多路复用器(Selector)**: Selector是Java NIO中的核心组件,...

    深入理解Apache Mina (6)---- Java Nio ByteBuffer与Mina ByteBuffer的区别

    3. 多种类型支持:Mina提供多种类型的ByteBuffer,如HeapByteBuffer、DirectByteBuffer和复合缓冲区,可以根据具体需求选择最合适的类型。 4. 零拷贝:Mina在某些情况下支持零拷贝技术,比如在NIO与Mina ByteBuffer...

    NIO trick and trap NIO网络

    - **HeapByteBuffer**:数据存储在JVM堆上,适用于小数据量或频繁访问的情况。 - **DirectByteBuffer**:数据存储在非JVM堆上,适用于大数据量传输,能有效减少数据复制带来的开销。 - **理解关键概念**:Capacity...

    Netty面试专题

    而在NIO的基础上,Netty进一步优化了网络编程模型,不仅提供了更加简洁的API接口,还增强了性能和可扩展性,使其成为现代高性能网络应用程序开发的理想选择。对于面试者来说,深入理解这些概念和技术细节对于成功...

    Netty面试专题1

    - **DirectByteBuffer**:直接内存缓冲区,数据直接存放在堆外内存,减少了系统空间到用户空间的拷贝,适用于大型、持久的缓冲区。 - **HeapByteBuffer**:数据存储在 JVM 堆内,由 JVM 管理,创建和销毁成本低,...

Global site tag (gtag.js) - Google Analytics