java中缓冲源码的分析

  

<强> java中缓冲源码的分析

  

<>强缓冲

  

缓冲区的类图如下:

  

癹ava中缓冲源码的分析"

  

除了布尔,其他基本数据类型都有对应的缓冲区,但是只有ByteBuffer才能和通道交互。只有ByteBuffer才能产生直接的缓冲区,其他数据类型的缓冲区只能产生堆类型的Buffer.ByteBuffer可以产生其他数据类型的视图缓冲区,如果ByteBuffer本身是直接的,则产生的各视图缓冲区也是直接的。

  

<强>直接和堆类型缓冲的本质

  

首选说说JVM是怎么进行IO操作的。

  

JVM在需要通过操作系统调用完成IO操作,比如可以通过读系统调用完成文件的读取.read的原型是:,和其他的IO系统调用类似,一般需要缓冲区作为其中一个参数,该缓冲区要求是连续的。

  

缓冲分为直接和堆两类,下面分别说明这两类缓冲区。

  

<强>堆

  

堆类型的缓冲存在于JVM的堆上,这部分内存的回收与整理和普通的对象一样.Heap类型的缓冲对象都包含一个对应基本数据类型的数组属性(比如:最后* * []hb),数组才是堆类型缓冲的底层缓冲区。
  

  

但是堆类型的缓冲区不能作为缓冲区参数直接进行系统调用,主要因为下面两个原因。

  
      <李> JVM在GC时可能会移动缓冲区(复制——整理),缓冲区的地址不固定。
      李   <李>系统调用时,缓冲区需要是连续的,但是数组可能不是连续的(JVM的实现没要求连续)。
      李   
  

所以使用堆类型的缓冲进行IO时,JVM需要产生一个临时直接类型的缓冲区,然后进行数据复制,再使用临时直接的

  

缓冲作为参数进行操作系统调用。这造成很低的效率,主要是因为两个原因:

  
      <李>需要把数据从堆类型的缓冲区里面复制到临时创建的直接的缓冲里面。   <李>可能产生大量的缓冲对象,从而提高GC的频率,所以在IO操作时,可以通过重复利用缓冲进行优化。
      李   
  

<强>直接

  

直接类型的缓冲区,不存在于堆上,而JVM是通过malloc直接分配的一段连续的内存,这部分内存成为直接内存,JVM进行IO系统调用时使用的是直接内存作为缓冲区。
  

  

- xx: MaxDirectMemorySize,通过这个配置可以设置允许分配的最大直接内存的大小(MappedByteBuffer分配的内存不受此配置影响)。
  

  

直接内存的回收和堆内存的回收不同,如果直接内存使用不当,很容易造成OutOfMemoryError.Java没有提供显示的方法去主动释放直接内存,sun.misc.Unsafe类可以进行直接的底层内存操作,通过该类可以主动释放和管理直接内存。同理,也应该重复利用直接内存以提高效率。

  

<强> MappedByteBuffer和DirectByteBuffer之间的关系

  

这是有点向后:应由权利MappedByteBuffer DirectByteBuffer的子类,但是保持规范明确、简单,优化的目的,很容易做到的。这个工作因为DirectByteBuffer是一个包的私有类。(本段话摘自MappedByteBuffer的源码)
  

  

实际上,MappedByteBuffer属于映射缓冲区(自己看看虚拟内存),但是DirectByteBuffer只是说明该部分内存是JVM在直接内存区分配的连续缓冲区,并不一是映射的。也就是说MappedByteBuffer应该是DirectByteBuffer的子类,但是为了方便和优化,把MappedByteBuffer作为了DirectByteBuffer的父类,另外,虽然MappedByteBuffer在逻辑上应该是DirectByteBuffer的子类,而且MappedByteBuffer的内存的GC和直接内存的GC类似(和GC堆不同),但是分配的MappedByteBuffer的大小不受- xx: MaxDirectMemorySize参数影响。
  

  

MappedByteBuffer封装的是内存映射文件操作,也就是只能进行文件IO操作.MappedByteBuffer是根据mmap产生的映射缓冲区,这部分缓冲区被映射到对应的文件页上,属于直接内存在用户态,通过MappedByteBuffer可以直接操作映射缓冲区,而这部分缓冲区又被映射到文件页上,操作系统通过对应内存页的调入和调出完成文件的写入和写出。

  

<强> MappedByteBuffer

  

通过FileChannel。地图(MapMode模式,长位置,尺寸)得到MappedByteBuffer、下面结合源码说明MappedByteBuffer的产生过程。

  

<强> FileChannel.map的源码:

        公共MappedByteBuffer地图(MapMode模式,长位置,尺寸)   抛出IOException   {   ensureOpen ();   如果(位置& lt;0 l)   把新IllegalArgumentException(“消极立场”);   如果(大小& lt;0 l)   把新IllegalArgumentException(“负大小”);   如果(位置+大小& lt;0)   把新IllegalArgumentException(“职位+大小溢出”);//最大2 g   如果(大小比;Integer.MAX_VALUE)   把新IllegalArgumentException(“大小超过Integer.MAX_VALUE”);   int将=1;   如果(模式==MapMode.READ_ONLY)   将=MAP_RO;   else if(模式==MapMode.READ_WRITE)   将=MAP_RW;   else if(模式==MapMode.PRIVATE)   将=MAP_PV;   断言(将在=0);   如果((模式!=MapMode.READ_ONLY),,!可写)   把新NonWritableChannelException ();   如果(!可读)   把新NonReadableChannelException ();      长addr=1;   int ti=1;   尝试{   开始();   ti=threads.add ();   如果(! isOpen ())   返回null;//尺寸()返回实际的文件大?/如果实际文件大小不符合,则增大文件的大小,文件的大小被改变,文件增大的部分默认设置为0。   如果(大小()& lt;位置+大小){//扩展文件大小   如果(!可写){   把新的IOException(“频道不开放写作”+   ”——不能扩展所需的文件大小”);   }   int房车;   {做//增大文件的大小   房车=nd。截断(fd,位置+大小);   },((rv==IOStatus.INTERRUPTED),,isOpen ());   }//如果要求映射的文件大小为0,则不调用操作系统的mmap调用,只是生成一个空间容量为0的DirectByteBuffer//并返回   如果(大?=0){   addr=0;//不需要一个有效的文件描述符   FileDescriptor假=new FileDescriptor ();   如果((!可写)| |(将==MAP_RO))   返回跑龙套。newMappedByteBufferR(0, 0,假,null);   其他的   返回跑龙套。newMappedByteBuffer(0, 0,假,null);   }//allocationGranularity的大小在我的系统上是4 k//页对齐,pagePosition为第多少页   int pagePosition=(int) (% allocationGranularity位置);//从页的最开始映射   长mapPosition=位置- pagePosition;//因为从页的最开始映射,增大映射空间   长mapSize=+ pagePosition大小;   尝试{//如果没有从map0异常被抛出,地址是有效的//本地方法,源代码在openjdk/jdk/src/solaris/本地/太阳/nio/ch/FileChannelImpl.c,//参见下面的说明   addr=map0(将mapPosition mapSize);   }捕捉(OutOfMemoryError x) {//一个OutOfMemoryError可能表明我们已经耗尽内存//所以力gc和re-attempt地图   system . gc ();   尝试{   thread . sleep (100);   }捕捉(InterruptedException y) {   .interrupt Thread.currentThread () ();   }   尝试{   addr=map0(将mapPosition mapSize);   }捕捉(OutOfMemoryError y) {//第二个内存溢出后,失败   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null

java中缓冲源码的分析