相信大家都知道,但是两者的区别在什么地方呢?在不同的环境下采用哪种类型的ByteBuffer会更有效率呢?
先解释一下两者的区别:
Non-directByteBuffer内存是分配在堆上的,直接由Java虚拟机负责垃圾收集,你可以把它想象成一个字节数组的包装类,如下伪码所示:
HeapByteBuffer extends ByteBuffer {
    byte[] content;
    int position, limit, capacity;
    ......
}
而DirectByteBuffer是通过JNI在Java虚拟机外的内存中分配了一块(所以即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的DirectByteBuffer),该内存块并不直接由Java虚拟机负责垃圾收集,但是在DirectByteBuffer包装类被回收时,会通过Java Reference机制来释放该内存块。如下伪码所示:
DirectByteBuffer extends ByteBuffer {
    long address;
    int position, limit, capacity;

protected void finalize() throws Throwable{
//释放内存块,该段代码仅仅用于演示,真正的DirectByteBuffer并不是通过finalize来释放的
        releaseAddress();  
        ......
    }
    ......
}
我相信大部分朋友们对上面的区别都应该很了解,那么还有什么其他的区别呢?嘿嘿,让我们稍微深入一点,翻到sun.nio.ch.IOUtil.java,绝大部分Channel类都是通过这个工具类和外界进行通讯的,如FileChannel/SocketChannel等等。我简单的用伪码把write方法给表达出来(read方法也差不多,就不多做说明了)
int write(ByteBuffer src, ......) {
    if (src instanceof DirectBuffer)
        return writeFromNativeBuffer(...);
   ByteBufferdirect = getTemporaryDirectBuffer(src);
    writeFromNativeBuffer(direct,......);
    updatePosition(src);
    releaseTemporaryDirectBuffer(direct);
}
是的,在发送和接收前会把Non-directByteBuffer转换为DirectByteBuffer,然后再进行相关的操作,最后更新原始ByteBuffer的position。这意味着什么?假设我们要从网络中读入一段数据,再把这段数据发送出去的话,

采用Non-directByteBuffer的流程是这样的:
网络 --> 临时的DirectByteBuffer --> 应用 Non-directByteBuffer --> 临时的DirectByteBuffer --> 网络

而采用DirectByteBuffer的流程是这样的:
网络 --> 应用 DirectByteBuffer --> 网络

可以看到,除开构造和析构临时DirectByteBuffer的时间外,起码还能节约两次内存拷贝的时间。那么是否在任何情况下都采用DirectBuffer呢?
不是。对于大部分应用而言,两次内存拷贝的时间几乎可以忽略不计,而构造和析构DirectBuffer的时间却相对较长。在JVM的实现当中,某些方法会缓存一部分临时DirectByteBuffer,意味着如果采用DirectByteBuffer仅仅能节约掉两次内存拷贝的时间,而无法节约构造和析构的时间。就用Sun的实现来说,write(ByteBuffer)和read(ByteBuffer)方法都会缓存临时DirectByteBuffer,而write(ByteBuffer[])和read(ByteBuffer[])每次都生成新的临时DirectByteBuffer。
根据这些区别,我会提出如下的建议:
·        如果你做中小规模的应用(在这里,应用大小是按照使用ByteBuffer的次数和规模来做划分的),而且并不在乎这该死的细节问题,请选择Non-directByteBuffer·        如果采用DirectByteBuffer后性能并没有出现你所期待的变化,请选择Non-directByteBuffer·        如果没有DirectByteBuffer Pool,尽量不要使用DirectByteBuffer·        除非你确定该ByteBuffer会长时间存在,并且和外界有频繁交互,可采用DirectByteBuffer·        如果采用Non-directByteBuffer,那么采用非聚集(gather)的write/read(ByteBuffer)效果反而可能超出聚集的write/read(ByteBuffer[]),因为聚集的write/read的临时DirectByteBuffer是非缓存的
基本上,采用Non-directByteBuffer总是对的!因为内存拷贝需要的开销对大部分应用而言都可以忽略不计。不过我做的是大规模的网络并发框架,因此对这些细节问题还是有必要有深入认识的,并且根据这些细节来调节自己的Buffer继承体系(再次抱怨,ByteBuffer无法扩展实在是一个非常非常非常费解的设计)
 
注:前面提到的“即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的DirectByteBuffer”中的可能是指可以通过-XX:MaxDirectMemorySize=<size>来指定DirectByteBuffer实例最多可以使用的内存总数。如指定-XX:MaxDirectMemorySize=1024,则系统中所有存活的DirectByteBuffer总内存数不能超过1024字节。

收藏:Non-direct与direct ByteBuffer区别的更多相关文章

  1. NIO中的heap Buffer和direct Buffer区别

    在Java的NIO中,我们一般采用ByteBuffer缓冲区来传输数据,一般情况下我们创建Buffer对象是通过ByteBuffer的两个静态方法: ByteBuffer.allocate(int c ...

  2. Tomcat 9内存溢出:"http-apr-8080-Acceptor-0" java.lang.OutOfMemoryError: Direct buffer memory

    Tomcat开启了APR模式,而APR模式会使用堆外内存,关于堆内存可从如下链接了解一下:http://blog.csdn.net/zhouhl_cn/article/details/6573213. ...

  3. Java IO 学习(六)Java的Direct Memory与IO

    ByteBuffer的源码中有这样一段注释: A byte buffer is either direct or non-direct. Given a direct byte buffer, the ...

  4. java.nio.ByteBuffer 以及flip,clear及rewind区别

    Buffer 类 定义了一个可以线性存放primitive type数据的容器接口.Buffer主要包含了与类型(byte, char…)无关的功能. 值得注意的是Buffer及其子类都不是线程安全的 ...

  5. RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较(转)

    RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储 RabbitMQ提供了四种Exchange:fanout,direct, ...

  6. 搞懂iobuffer就得先学习bytebuffer

    ByteBuffer前前后后看过好几次了,实际使用也用了一些,总觉得条理不够清晰. <程序员的思维修炼>一本书讲过,主动学习,要比单纯看资料效果来的好,所以干脆写个详细点的文章来记录一下. ...

  7. ByteBuffer: 图解ByteBuffer(转)

    ByteBuffer前前后后看过好几次了,实际使用也用了一些,总觉得条理不够清晰. <程序员的思维修炼>一本书讲过,主动学习,要比单纯看资料效果来的好,所以干脆写个详细点的文章来记录一下. ...

  8. 【MINA】缓存区ByteBuffer和IOBuffer你要了解的常用知识

    mina中IOBuffer是Nio中ByteBuffer的衍生类,主要是解决Bytebuffer的两个不足 1.没有提供足够灵活的get/putXXX方法 2.它容量固定,难以写入可变长度的数据 特点 ...

  9. 比较DirectX和OpenGL的区别(比较详细)

    OpenGL是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库.OpenGL的前身是SGI公司为其图形工作站开发的IRIS GL.IRIS GL是一个工业标准的3D图形软件接口,功能虽然 ...

随机推荐

  1. vs2017安装

    每次安装包都搞的很大,而且出各式各式的问题. 安装程序清单签名失败 运行'vs_Enterprise.exe'时,出现'安装程序清单签名失败'的错误,直接删除'vs_installer.opc'文件, ...

  2. 通过容器提交镜像(docker commit)以及推送镜像(docker push)笔记

    在本地创建一个容器后,可以依据这个容器创建本地镜像,并可把这个镜像推送到Docker hub中,以便在网络上下载使用. 查看镜像 [root@docker-test1 ~]# docker image ...

  3. Centos下SVN环境部署记录

    大多数情况下,我们日常工作中用的版本控制系统都会选择分布式的Git,它相比于集中式的SVN有很多优势.但是有些项目软件基于自身限制,可能只支持SVN做工程同步.废话就不多说了,下面记录下SVN的部署和 ...

  4. HTML 5 拖放

    拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放. 拖放事件 1. ...

  5. "Linux内核分析"第七周

    可执行程序的装载 张文俊+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.预 ...

  6. githup地址

    githup地址:https://github.com/caowenjing/test.git

  7. PHP文件系统操作常用函数

    虽然PHP提供很多内置的文件处理函数,但是分得特别细,有一些操作需要多个函数一起使用才能达到目标,比如删除非空文件夹的所有内容,遍历文件夹等功能,下面各个函数是学习的时候整理的,有的是教程里的,有的是 ...

  8. Java7和8在虚拟机上的差异:Perm Generation vs. Metaspace

  9. ejabberd在windows10下的配置文件ejabberd.yml存放路径

    cd %USERPROFILE%\AppData\Roaming\ejabberd\conf C:\Users\Administrator\AppData\Roaming\ejabberd\conf

  10. Activiti的部署问题

    http://www.kafeitu.me/activiti/2012/03/22/workflow-activiti-action.html 既可以通过每次Spring应用程序启动时,执行部署命令. ...