Java中ByteBuffer的作用是什么?

223

ByteBuffer在Java中有哪些应用示例?请列举任何使用此功能的示例场景。


2
Apache Hadoop的压缩(例如zlib编解码器)使用Java.nio中的ByteBuffer。 - nikk
1
适用于将数据发送到GPU。 - user1300214
1
StackOverFlow: "此问题无用,因此已关闭。" 我:UpVote - Mohammad Elsayed
5个回答

155

这篇文章很好地描述了它的用途和缺点。当您需要进行快速低级别的I/O操作时,您基本上可以使用它。如果您要实现TCP/IP协议或编写数据库(DBMS),那么这个类会很有用。


4
在使用Java和Cassandra数据库开发的高流量网站中,它有任何用处吗? - Aklin
1
经过快速搜索,看起来Cassandra确实使用ByteBuffer:http://www.mail-archive.com/commits@cassandra.apache.org/msg14967.html 如果你想知道是否有原始网站,可能会有,但通常只会被网站间接使用-通过使用像cassandra这样的工具。 - kelloti
2
不错的链接,谢谢。我想添加一个我发现很有用的 - https://worldmodscode.wordpress.com/2012/12/14/the-java-bytebuffer-a-crash-course/ - Peter Perháč
很抱歉,链接无效... - RoyalBigMack

103

ByteBuffer类很重要,因为它构成了Java通道使用的基础。ByteBuffer类在Java 7文档中定义了6个字节缓冲区操作类别如下所述

绝对和相对的获取放置方法,可以读取和写入单个字节; 相对批量获取方法,可将连续的字节序列从此缓冲区传输到数组中; 相对批量放置方法,可将来自字节数组或其他字节缓冲区的连续字节序列传输到此缓冲区中; 绝对和相对的获取和放置方法,可读取和写入其他原始类型的值,并将它们转换为特定字节顺序的字节序列; 创建视图缓冲区的方法,允许将字节缓冲区视为包含某些其他原始类型值的缓冲区;以及 用于压缩, 复制切片 字节缓冲区的方法。

示例代码:将字节放入缓冲区。

    // Create an empty ByteBuffer with a 10 byte capacity
    ByteBuffer bbuf = ByteBuffer.allocate(10);

    // Get the buffer's capacity
    int capacity = bbuf.capacity(); // 10

    // Use the absolute put(int, byte).
    // This method does not affect the position.
    bbuf.put(0, (byte)0xFF); // position=0

    // Set the position
    bbuf.position(5);

    // Use the relative put(byte)
    bbuf.put((byte)0xFF);

    // Get the new position
    int pos = bbuf.position(); // 6

    // Get remaining byte count
    int rem = bbuf.remaining(); // 4

    // Set the limit
    bbuf.limit(7); // remaining=1

    // This convenience method sets the position to 0
    bbuf.rewind(); // remaining=7

59
这条评论的大部分内容来自于java 7 docs - Janus Troelsen
6
除非您正在利用非明显的副作用,否则我认为您可能正在错误地使用绝对放置的方法。Javadoc似乎表明,绝对放置需要一个索引值。 - Chuck Wolber
8
即使只是API文档的拷贝,它也值得得分,因为一些人似乎懒得去查看。请注意不要改变原意。 - Chris
1
其中一个示例中有错误。 "absolute put"缺少表示索引的第一个参数(在此情况下为0)。 - bvdb

29

使用基于流的API进行Java IO时,需要使用缓冲区作为数据在用户空间内的临时存储。通过DMA从磁盘读取的数据首先被复制到内核空间中的缓冲区,然后再传输到用户空间中的缓冲区。因此存在开销。避免这种情况可以在性能方面获得可观的收益。

如果有一种直接访问内核空间缓冲区的方法,我们就可以跳过用户空间的临时缓冲区。Java NIO提供了一种这样的方式。

ByteBuffer是Java NIO提供的几个缓冲区之一。它只是一个容器或储存器,用于读取或写入数据。通过在Buffer上使用allocateDirect() API分配直接缓冲区来实现上述行为。

Java ByteBuffer文档包含有用的信息。


22

这里有一篇很棒的文章介绍了ByteBuffer的好处。以下是该文章中的要点:

  • 无论是直接的还是间接的,ByteBuffer的第一个优势都在于能够高效地随机访问结构化二进制数据(例如,在答案中所述的低层IO操作)。在Java 1.4之前,读取此类数据可以使用DataInputStream,但没有随机访问。

以下是专门针对direct ByteBuffer/MappedByteBuffer的好处。请注意,Direct Buffers是在堆外创建的:

  1. 不受垃圾回收循环影响:直接缓冲区不会在垃圾回收循环期间移动,因为它们存储在堆外。 TerraCota的BigMemory缓存技术似乎严重依赖这个优势。如果它们在堆上,将会减慢gc暂停时间。

  2. 性能提升:在流式IO中,读取调用将涉及系统调用,需要在用户模式和内核模式之间进行上下文切换,反之亦然,特别是如果文件被不断访问。但是,通过内存映射,这种上下文切换得到了减少,因为数据更有可能在内存中被找到(MappedByteBuffer)。如果数据在内存中可用,则直接访问而不涉及操作系统,即没有上下文切换。

请注意,如果文件很大且少数块组被更频繁地访问,则MappedByteBuffers非常有用。

  1. 页面共享:内存映射文件可以在进程之间共享,因为它们分配在进程的虚拟内存空间中,并且可以在进程之间共享。

16

在Android中,您可以使用directAlloc方法在C++和Java之间创建共享缓冲区,并且可以在两个方向上操作该缓冲区。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接