`

ByteBuffer深入学习

 
阅读更多

背景

 

最近在研究netty的源代码,发现netty的内存管理都是用jdk的ByteBuffer。为了更深入的了解bytebuffer,因此有了这篇文章

 

ByteBuffer的基本组成

ByteBuffer 的基本函数http://kakajw.iteye.com/blog/1797073



ByteBuffer分为两类DirectBuffer和HeapBuffer。DirectBuffer速度快于HeapBuffer。DirectBuffer可以直接将数据输出到终端,避免内存拷贝。DirectBuffer的分配开销大于HeapBuffer。但是DirectBuffer不用GC。因此DirectBuffer一般用于大块,且长期使用的内存。因此特别适用于Netty的内存分配模型。详细的说明如下:

* <p> A byte buffer is either <i>direct</i> or <i>non-direct</i>.  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.
 *
 * <p> A direct byte buffer may be created by invoking the {@link
 * #allocateDirect(int) 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.
 *
 * <p> A direct byte buffer may also be created by {@link
 * java.nio.channels.FileChannel#map </code>mapping<code>} 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.
 *
 * <p> Whether a byte buffer is direct or non-direct may be determined by
 * invoking its {@link #isDirect isDirect} method.  This method is provided so
 * that explicit buffer management can be done in performance-critical code.

    ByteBuffer继承于Buffer对象,Buffer的成员变量和方法如下:

public abstract class Buffer {

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1; //标记的位置
    private int position = 0; //当前位置
    private int limit;            //限制大小
    private int capacity;       //总的内存容量

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

        /**
     * Sets this buffer's mark at its position. </p>
     *
     * @return  This buffer
     */
    public final Buffer mark() {
        mark = position;
        return this;
    }

    /**
     * Resets this buffer's position to the previously-marked position.
     *
     * <p> Invoking this method neither changes nor discards the mark's
     * value. </p>
     *
     * @return  This buffer
     *
     * @throws  InvalidMarkException
     *          If the mark has not been set
     */
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

    /**
     * Clears this buffer.  The position is set to zero, the limit is set to
     * the capacity, and the mark is discarded.
     *
     * <p> Invoke this method before using a sequence of channel-read or
     * <i>put</i> operations to fill this buffer.  For example:
     *
     * <blockquote><pre>
     * buf.clear();     // Prepare buffer for reading
     * in.read(buf);    // Read data</pre></blockquote>
     *
     * <p> This method does not actually erase the data in the buffer, but it
     * is named as if it did because it will most often be used in situations
     * in which that might as well be the case. </p>
     *
     * @return  This buffer
     */
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }


    /**
     * Flips this buffer.  The limit is set to the current position and then
     * the position is set to zero.  If the mark is defined then it is
     * discarded.
     *
     * <p> After a sequence of channel-read or <i>put</i> operations, invoke
     * this method to prepare for a sequence of channel-write or relative
     * <i>get</i> operations.  For example:
     *
     * <blockquote><pre>
     * buf.put(magic);    // Prepend header
     * in.read(buf);      // Read data into rest of buffer
     * buf.flip();        // Flip buffer
     * out.write(buf);    // Write header + data to channel</pre></blockquote>
     *
     * <p> This method is often used in conjunction with the {@link
     * java.nio.ByteBuffer#compact compact} method when transferring data from
     * one place to another.  </p>
     *
     * @return  This buffer
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * Rewinds this buffer.  The position is set to zero and the mark is
     * discarded.
     *
     * <p> Invoke this method before a sequence of channel-write or <i>get</i>
     * operations, assuming that the limit has already been set
     * appropriately.  For example:
     *
     * <blockquote><pre>
     * out.write(buf);    // Write remaining data
     * buf.rewind();      // Rewind buffer
     * buf.get(array);    // Copy data into array</pre></blockquote>
     *
     * @return  This buffer
     */
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
}

   ByteBuffer的主要接口

public abstract class ByteBuffer
    extends Buffer
    implements Comparable<ByteBuffer>
{

    // These fields are declared here rather than in Heap-X-Buffer in order to
    // reduce the number of virtual method invocations needed to access these
    // values, which is especially costly when coding small buffers.
    //
    final byte[] hb;                  // Non-null only for heap buffers
    final int offset;
    boolean isReadOnly;                 // Valid only for heap buffers

 /**
     * Creates a new byte buffer whose content is a shared subsequence of
     * this buffer's content.
     *
     * <p> The content of the new buffer will start at this buffer's current
     * position.  Changes to this buffer's content will be visible in the new
     * buffer, and vice versa; the two buffers' position, limit, and mark
     * values will be independent.
     *
     * <p> The new buffer's position will be zero, its capacity and its limit
     * will be the number of bytes remaining in this buffer, and its mark
     * will be undefined.  The new buffer will be direct if, and only if, this
     * buffer is direct, and it will be read-only if, and only if, this buffer
     * is read-only.  </p>
     *
     * @return  The new byte buffer
     */
    public abstract ByteBuffer slice();

    /**
     * Creates a new byte buffer that shares this buffer's content.
     *
     * <p> The content of the new buffer will be that of this buffer.  Changes
     * to this buffer's content will be visible in the new buffer, and vice
     * versa; the two buffers' position, limit, and mark values will be
     * independent.
     *
     * <p> The new buffer's capacity, limit, position, and mark values will be
     * identical to those of this buffer.  The new buffer will be direct if,
     * and only if, this buffer is direct, and it will be read-only if, and
     * only if, this buffer is read-only.  </p>
     *
     * @return  The new byte buffer
     */
    public abstract ByteBuffer duplicate();

    /**
     * Creates a new, read-only byte buffer that shares this buffer's
     * content.
     *
     * <p> The content of the new buffer will be that of this buffer.  Changes
     * to this buffer's content will be visible in the new buffer; the new
     * buffer itself, however, will be read-only and will not allow the shared
     * content to be modified.  The two buffers' position, limit, and mark
     * values will be independent.
     *
     * <p> The new buffer's capacity, limit, position, and mark values will be
     * identical to those of this buffer.
     *
     * <p> If this buffer is itself read-only then this method behaves in
     * exactly the same way as the {@link #duplicate duplicate} method.  </p>
     *
     * @return  The new, read-only byte buffer
     */
    public abstract ByteBuffer asReadOnlyBuffer();
}

 MappedByteBuffer 实现了ByteBuffer接口,内存映射到文件

/**
 * A direct byte buffer whose content is a memory-mapped region of a file.
 *
 * <p> Mapped byte buffers are created via the {@link
 * java.nio.channels.FileChannel#map FileChannel.map} method.  This class
 * extends the {@link ByteBuffer} class with operations that are specific to
 * memory-mapped file regions.
 *
 * <p> A mapped byte buffer and the file mapping that it represents remain
 * valid until the buffer itself is garbage-collected.
 *
 * <p> The content of a mapped byte buffer can change at any time, for example
 * if the content of the corresponding region of the mapped file is changed by
 * this program or another.  Whether or not such changes occur, and when they
 * occur, is operating-system dependent and therefore unspecified.
 *
 * <a name="inaccess"><p> All or part of a mapped byte buffer may become
 * inaccessible at any time, for example if the mapped file is truncated.  An
 * attempt to access an inaccessible region of a mapped byte buffer 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.  It is
 * therefore strongly recommended that appropriate precautions be taken to
 * avoid the manipulation of a mapped file by this program, or by a
 * concurrently running program, except to read or write the file's content.
 *
 * <p> Mapped byte buffers otherwise behave no differently than ordinary direct
 * byte buffers. </p>
 *
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class MappedByteBuffer
    extends ByteBuffer
{

    // This is a little bit backwards: By rights MappedByteBuffer should be a
    // subclass of DirectByteBuffer, but to keep the spec clear and simple, and
    // for optimization purposes, it's easier to do it the other way around.
    // This works because DirectByteBuffer is a package-private class.

    // For mapped buffers, a FileDescriptor that may be used for mapping
    // operations if valid; null if the buffer is not mapped.
    private final FileDescriptor fd;
    
     /**
     * Tells whether or not this buffer's content is resident in physical
     * memory.
     *
     * <p> A return value of <tt>true</tt> implies that it is highly likely
     * that all of the data in this buffer is resident in physical memory and
     * may therefore be accessed without incurring any virtual-memory page
     * faults or I/O operations.  A return value of <tt>false</tt> does not
     * necessarily imply that the buffer's content is not resident in physical
     * memory.
     *
     * <p> The returned value is a hint, rather than a guarantee, because the
     * underlying operating system may have paged out some of the buffer's data
     * by the time that an invocation of this method returns.  </p>
     *
     * @return  <tt>true</tt> if it is likely that this buffer's content
     *          is resident in physical memory
     */
    public final boolean isLoaded() {
        checkMapped();
        if ((address == 0) || (capacity() == 0))
            return true;
        long offset = mappingOffset();
        long length = mappingLength(offset);
        return isLoaded0(mappingAddress(offset), length, Bits.pageCount(length));
    }

    // not used, but a potential target for a store, see load() for details.
    private static byte unused;

    /**
     * Loads this buffer's content into physical memory.
     *
     * <p> This method makes a best effort to ensure that, when it returns,
     * this buffer's content is resident in physical memory.  Invoking this
     * method may cause some number of page faults and I/O operations to
     * occur. </p>
     *
     * @return  This buffer
     */
    public final MappedByteBuffer load() {
        checkMapped();
        if ((address == 0) || (capacity() == 0))
            return this;
        long offset = mappingOffset();
        long length = mappingLength(offset);
        load0(mappingAddress(offset), length);

        // Read a byte from each page to bring it into memory. A checksum
        // is computed as we go along to prevent the compiler from otherwise
        // considering the loop as dead code.
        Unsafe unsafe = Unsafe.getUnsafe();
        int ps = Bits.pageSize();
        int count = Bits.pageCount(length);
        long a = mappingAddress(offset);
        byte x = 0;
        for (int i=0; i<count; i++) {
            x ^= unsafe.getByte(a);
            a += ps;
        }
        if (unused != 0)
            unused = x;

        return this;
    }

    /**
     * Forces any changes made to this buffer's content to be written to the
     * storage device containing the mapped file.
     *
     * <p> If the file mapped into this buffer resides on a local storage
     * device then when this method returns it is guaranteed that all changes
     * made to the buffer since it was created, or since this method was last
     * invoked, will have been written to that device.
     *
     * <p> If the file does not reside on a local device then no such guarantee
     * is made.
     *
     * <p> If this buffer was not mapped in read/write mode ({@link
     * java.nio.channels.FileChannel.MapMode#READ_WRITE}) then invoking this
     * method has no effect. </p>
     *
     * @return  This buffer
     */
    public final MappedByteBuffer force() {
        checkMapped();
        if ((address != 0) && (capacity() != 0)) {
            long offset = mappingOffset();
            force0(fd, mappingAddress(offset), mappingLength(offset));
        }
        return this;
    }
}

   DirectByteBuffer继承MappedByteBuffer

class DirectByteBuffer
    extends MappedByteBuffer
    implements DirectBuffer
{



    // Cached unsafe-access object
    protected static final Unsafe unsafe = Bits.unsafe();

    // Cached array base offset
    private static final long arrayBaseOffset = (long)unsafe.arrayBaseOffset(byte[].class);

    // Cached unaligned-access capability
    protected static final boolean unaligned = Bits.unaligned();

    // Base address, used in all indexing calculations
    // NOTE: moved up to Buffer.java for speed in JNI GetDirectBufferAddress
    //    protected long address;

    // An object attached to this buffer. If this buffer is a view of another
    // buffer then we use this field to keep a reference to that buffer to
    // ensure that its memory isn't freed before we are done with it.
    private final Object att;

    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            //分配内存空间
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        //设置内存为0
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        //设置内存回收器
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

  //用剩下的空间创建一个直接缓存 
  public ByteBuffer slice() {
        int pos = this.position();
        int lim = this.limit();
        assert (pos <= lim);
        int rem = (pos <= lim ? lim - pos : 0);
        int off = (pos << 0);
        assert (off >= 0);
        return new DirectByteBuffer(this, -1, 0, rem, rem, off);
    }
    //拷贝一个直接缓存
    public ByteBuffer duplicate() {
        return new DirectByteBuffer(this,
                                              this.markValue(),
                                              this.position(),
                                              this.limit(),
                                              this.capacity(),
                                              0);
    }

 private short getShort(long a) {
        if (unaligned) { //不对齐的系统,不能通过一次获取多个字节
            short x = unsafe.getShort(a);
            return (nativeByteOrder ? x : Bits.swap(x));
        }
        return Bits.getShort(a, bigEndian);
    }

    public short getShort() {
        return getShort(ix(nextGetIndex((1 << 1))));
    }
 

    private ByteBuffer putShort(long a, short x) {
        if (unaligned) {
            short y = (x);
            unsafe.putShort(a, (nativeByteOrder ? y : Bits.swap(y)));
        } else {
            Bits.putShort(a, x, bigEndian);
        }
        return this;
    }

    public ByteBuffer putShort(short x) {

        putShort(ix(nextPutIndex((1 << 1))), x);
        return this;
    }

}

 

   HeapByteBuffer是堆缓存,通过new byte[cap]方法分配。

class HeapByteBuffer
    extends ByteBuffer
{

    // For speed these fields are actually declared in X-Buffer;
    // these declarations are here as documentation
    /*

    protected final byte[] hb;
    protected final int offset;

    */

    HeapByteBuffer(int cap, int lim) {            // package-private

        super(-1, 0, lim, cap, new byte[cap], 0);
        /*
        hb = new byte[cap];
        offset = 0;
        */
    }


    public short getShort() {
        return Bits.getShort(this, ix(nextGetIndex(2)), bigEndian);
    }

    public short getShort(int i) {
        return Bits.getShort(this, ix(checkIndex(i, 2)), bigEndian);
    }

    public ByteBuffer putShort(short x) {
        Bits.putShort(this, ix(nextPutIndex(2)), x, bigEndian);
        return this;
    }

    public ByteBuffer putShort(int i, short x) {
        Bits.putShort(this, ix(checkIndex(i, 2)), x, bigEndian);
        return this;
    }
}
 

 

 

 

 

  • 大小: 11.6 KB
分享到:
评论

相关推荐

    Java NIO学习笔记——ByteBuffer用法

    ByteBuffer的用法是Java NIO学习中的核心内容。 首先,我们了解下ByteBuffer的基本概念。ByteBuffer是一个字节缓冲区,可以存储字节序列。在NIO中,所有的数据读写都通过缓冲区进行,ByteBuffer与其他类型的Buffer...

    【IT十八掌徐培成】Java基础第26天-05.ByteBuffer-mark-pos-limit-cap-flip.zip

    在Java编程语言中,`ByteBuffer`是Java NIO(New IO)框架中的核心类之一,它提供了一种高效处理字节数据的方式。...通过深入学习和实践,我们可以利用Java NIO提供的强大功能,编写出更加灵活和高效的代码。

    易语言汇编版ByteBuffer源码

    易语言汇编版ByteBuffer...总之,易语言汇编版ByteBuffer源码是一个结合了易语言和汇编语言优势的实用工具,对于进行网络编程的开发者来说,它提供了高效的数据处理手段,同时也为学习者提供了深入理解底层机制的机会。

    netty深入学习资料(java源代码实例)

    1. **ServerBootstrap**配置:学习如何设置服务器启动参数,如绑定端口、配置EventLoopGroup等。 2. **ChannelHandler**编写:实现自定义处理器,处理进来的数据和事件。 3. **Pipeline构建**:了解如何定制处理链,...

    字节流字符流

    通过阅读和理解这些代码,我们可以深入学习如何在Java中运用字节流和字符流进行文件操作,以及如何在需要时进行两者之间的转换。 总的来说,字节流和字符流是Java I/O系统的重要组成部分,理解它们的工作原理和应用...

    深入理解Apache Mina

    本资料集合对Apache Mina进行了深入探讨,涵盖了Mina的关键组件和概念,包括IoFilter、IoHandler、ByteBuffer以及线程模型等。 1. **IoFilter与IoHandler**: IoFilter是Mina中的过滤器机制,它允许在数据传输过程...

    mina2资料-各种教程

    了解以上知识点是掌握Apache MINA的基础,通过提供的学习资料,如《Mina2.0学习笔记》、《Apache MINA入门基础》和《Apache MINA Server 2.0中文参考手册》等,可以深入学习MINA的API用法、设计原理和最佳实践。...

    深入浅出Netty ppt

    接着,我们深入学习Netty的异步模型。Netty采用非阻塞I/O,通过EventLoopGroup管理多个线程,每个线程负责处理多个连接,从而实现高效的并发处理。这种模型大大减少了线程切换的开销,提高了系统性能。 Netty的协议...

    深入浅出Netty

    对于想要深入学习Netty的开发者来说,理解并掌握以下知识点至关重要: 1. Netty的EventLoop和EventLoopGroup:了解线程模型和任务调度机制。 2. Channel和ChannelHandlerContext:理解数据传输的载体和上下文信息。...

    Netty 高并发深入浅出学习高并发服务器

    通过深入学习 Netty,开发者可以构建出高效、稳定的高并发服务器,实现复杂网络应用的需求。《深入浅出Netty.pdf》这本书很可能会详细讲解这些知识点,包括理论基础、实践示例以及高级特性的使用,对于理解和掌握 ...

    netty 权威指南2 源码

    总的来说,《Netty 权威指南2》的源码部分会涉及网络编程基础、并发模型、内存管理、协议处理等多个方面的知识,是一份深入学习和研究Netty不可多得的资料。通过深入学习,开发者不仅能掌握Netty的使用,还能具备...

    DMS技术(源代码)

    通过对这些文件的分析和理解,开发者可以深入学习到如何设计和实现一个完整的数据管理系统,包括客户端和服务器端的交互机制、数据库操作、以及如何使用Java和相关框架来构建高效稳定的应用。此外,对于有兴趣进一步...

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

    本书《Java NIO Programming Cookbook》旨在深入浅出地介绍如何利用Java NIO进行高效的I/O编程,并提供了源码供读者实践和学习。 在Java NIO中,`ByteBuffer`是核心类之一,它用于存储和读写数据。`...

    java 之异步套接字编程实例(AIO)

    Java中的异步套接字编程,也称为非阻塞I/O...在提供的`src`压缩包文件中,可能包含了实现上述AIO示例的源代码,通过阅读和理解这些代码,开发者可以深入学习Java AIO的工作原理和用法,从而提升在网络编程领域的技能。

    java NOI 学习

    ### Java NIO 学习 #### 一、Java NIO 的背景及意义 在Java早期版本(JDK 1.4之前),IO操作主要依赖于`java.io`包中的流式(stream-based)API,这类API是阻塞式的。虽然对于大多数日常应用场景而言,这种模型...

    netty-starter.zip

    在深入学习 Netty 之前,首先理解其基本概念至关重要。Netty 是基于 Java NIO(非阻塞I/O)构建的,它的设计目标是提供一个高效、灵活且易于使用的网络通信框架。NIO 允许一个线程处理多个连接,避免了传统阻塞 I/O ...

    Java Socket学习---nio实现阻塞多线程通信

    本篇文章将深入探讨如何使用Java NIO(非阻塞I/O)来实现阻塞多线程通信,这对于高性能服务器端应用尤其重要。我们将会分析`EchoServer.java`、`EchoClient.java`和`SocketUtils.java`这三个文件中的关键知识点。 ...

    android 硬编码示例

    在Android开发中,硬编码(Hard Coding)通常指的是将数据、配置或逻辑直接写入代码,而不是通过外部文件或资源来动态获取。...对于想要深入学习Android多媒体处理和编码的开发者来说,这是一个宝贵的资源。

    nio相关知识学习

    在Java编程领域,NIO(New Input/Output)是一个重要的概念,它为处理大量输入输出提供了更为高效的方式,相比传统的IO模型,NIO具有非阻塞、多路复用...通过深入学习和实践,开发者可以更好地应对高并发场景下的挑战。

    Netty那点事(2)Netty中的bufferJava开

    在Java开发领域,Netty作为一个...开发者应深入学习其设计理念和操作方式,以便在实际项目中更好地利用Netty提供的强大功能。通过掌握ByteBuf,不仅可以优化内存使用,还能提高代码的可读性和维护性,降低系统复杂性。

Global site tag (gtag.js) - Google Analytics