第二章 缓冲区(13)

来源:互联网 发布:为什么淘宝口令打不开 编辑:程序博客网 时间:2024/05/16 07:23

2.4.2 直接buffer对象

ByteBuffer对象跟其他类型的buffer最大的不同是,Bytebuffer可以作为Channel通道类处理IO的输入或输出。如果你直接跳到第三章,你会发现Channel类只接受ByteBuffer类参数。

在第一章中我们提到,操作系统的IO操作是在内存中进行的。这些内存区域目前我们可以认为是连续的字节。因此不难发现为什么ByteBuffer是最适合处理IO操作的原因。你应该还记得,我们的操作系统会直接访问JVM进程的进程空间来传递数据。这也意味着,接受数据的buffer也必须是连续的字节内存空间。在JVM中,字节数组可能不是连续的字节空间,或者垃圾收集器可能随时移动字节数组的位置。同时,数组在java中是以对象的形式存在,而存储在对象中的数据可能随着不同JVM实现表现出不同的存储方式。

正是因为以上的原因,直接缓冲的概念被引进出来。直接缓冲主要是用来让通道(Channel)和本地IO模块打交道的。直接缓冲让通道直接调用本地方法来告诉操作系统使用通道能够直接访问的内存区域。

直接字节缓冲通常是IO操作最好的选择。它们被设计成JVM中最高效的IO操作机制。非直接缓冲也可以被传送给通道(Channel),但是这样可能导致一些性能的丢失。通常将非直接缓冲作为本地IO操作的目的地是不可能的。如果你传递一个非直接缓冲的ByteBuffer对象给通道(Channel),通道可能隐示的作了下面的调用:

1.创建一个临时的直接ByteBuffer对象。

2.将非直接缓冲的内容复制到临时缓冲中。

3.对临时缓冲执行底层IO操作。

4.程序走出临时对象的作用区域,最后变为垃圾被回收。

这可能导致潜在的缓冲区复制和对象拷贝,而这些都是我们想办法避免的。但是根据具体不同的实现,这可能并没有这么糟糕。运行环境可能缓存和重复利用这些直接缓冲或者执行一些其他的技巧来提高性能。如果你仅仅是使用一次这样的调用,运行环境优化并不会带来什么改变。另一方面说,如果你需要将你的buffer高负荷的重复使用,那么你就还是使用直接缓冲吧。

直接缓冲对IO是最佳的选择,但创建它却比非直接缓冲昂贵。直接缓冲使用的内存是通过调用本地代码产生的,内存分配绕过了JVM的堆。因此无论是建立还是销毁直接缓冲都要比在JVM本地堆中的缓冲区的代价高昂。直接缓冲的内存因为驻留在JVM堆之外,也不受JVM垃圾收集器的管束。

直接缓冲与非直接缓冲的性能会因为JVM、操作系统和代码设计而产生巨大差异。由于直接缓冲使用本地堆之外的内存,因此你的程序将依赖JVM之外的代码而是程序不可控。当为系统增加新功能时,确认你达到了你想要的结果。我推荐一条陈旧的软件格言:先让它工作,再加快它的运行。不要一开始就过多的考虑系统的优化,反而应该去关注系统的正确性。JVM的具体实现可能会为你考虑必要的优化,而使你做少量的工作就可以达到事半功倍的效果。

直接缓冲ByteBuffer是通过调用ByteBuffer.allocateDirect方法产生,跟前面介绍的allocate方法一样。还记得wrap方法产生的buffer对象不?这些对象通常都是非直接缓冲。

Public abstract class ByteBuffer 

Extends Buffer implements{

Public static ByteBuffer allocate(int capacity);

Public static ByteBuffer allocateDirect(int capacity);

Public abstract boolean isDirect();

}

所有的Buffer对象都提供了一个isDirect方法,来测试这个对象是不是直接缓冲区对象。因为只有ByteBuffer才能够分配直接缓冲区,如果想让非字节类型buffer调用isDirect方法返回true,那就看看下面的视图Buffer吧。

原创粉丝点击