NIO优势?堆内存?直接内存?映射文件?Transfer to?

一、操作系统相关基础

  • 在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数 read()、write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,然 后再把数据从内核IO缓冲区拷贝到进程的私有地址空间中去,这样便完成了一次IO操作。

至于为什么要多此一举搞一个内核IO缓冲区把原本只需一次拷贝数据 的事情搞成需要2次数据拷贝呢? 我想学过操作系统或者计算机系统结构的人都知道,这么做是为了减少磁盘的IO操作,为了提高性能而考虑的,因为我们的程序访问一般都带有局部性,也就是所 谓的局部性原理,在这里主要是指的空间局部性,即我们访问了文件的某一段数据,那么接下去很可能还会访问接下去的一段数据,由于磁盘IO操作的速度比直接 访问内存慢了好几个数量级,所以OS根据局部性原理会在一次 read()系统调用过程中预读更多的文件数据缓存在内核IO缓冲区中,当继续访问的文件数据在缓冲区中时便直接拷贝数据到进程私有空间,避免了再次的低 效率磁盘IO操作。

  • BufferedInputStream减少系统调用

JAVA虚拟机内部便会调用OS底层的 read()系统调用完成操作,如上所述,在第二次调用 in.read()的时候可能就是从内核缓冲区直接返回数据了(可能还有经过 native堆做一次中转,因为这些函数都被声明为 native,即本地平台相关,所以可能在C代码中有做一次中转,如 win32中是通过 C代码从OS读取数据,然后再传给JVM内存)。既然如此,JAVA的IO包中为啥还要提供一个 BufferedInputStream 类来作为缓冲区呢。关键在于四个字,"系统调用"!当读取OS内核缓冲区数据的时候,便发起了一次系统调用操作(通过native的C函数调用),而系统 调用的代价相对来说是比较高的,涉及到进程用户态和内核态的上下文切换等一系列操作,所以我们经常采用如下的包装:

FileInputStream in = new FileInputStream("D:\\java.txt"); 
BufferedInputStream buf_in = new BufferedInputStream(in);  
buf_in.read(); 

这样一来,我们每一次 buf_in.read() 时候,BufferedInputStream 会根据情况自动为我们预读更多的字节数据到它自己维护的一个内部字节数组缓冲区中,这样我们便可以减少系统调用次数,从而达到其缓冲区的目的。所以要明确 的一点是 BufferedInputStream 的作用不是减少 磁盘IO操作次数(这个OS已经帮我们做了),而是通过减少系统调用次数来提高性能的。同理 BufferedOuputStream , BufferedReader/Writer 也是一样的。在 C语言的函数库中也有类似的实现,如 fread(),这个函数就是 c语言中的缓冲IO,作用与BufferedInputStream()相同.

二、与传统I/O流相比,NIO的HeapByteBuffer有什么优势?

  • 开始讲NIO之前,了解为什么会有NIO,相比传统流I/O的优势在哪,它可以用来做什么等等的问题,还是很有必要的。

传统流I/O是基于字节的,所有I/O都被视为单个字节的移动;而NIO是基于块的,大家可能猜到了,NIO的性能肯定优于流I/O。没错!其性能的提高 要得益于其使用的结构更接近操作系统执行I/O的方式:通道和缓冲器。我们可以把它想象成一个煤矿,通道是一个包含煤层(数据)的矿藏,而缓冲器则是派送 到矿藏的卡车。卡车载满煤炭而归,我们再从卡车上获得煤炭。也就是说,我们并没有直接和通道交互;我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么 从缓冲器获得数据,要么向缓冲器发送数据。(这段比喻出自Java编程思想)

三、内存映射文件MappedByteBuffer(和DirectByteBuffer不同,少了将数据拷贝到OS内核缓冲区这一步)

  • 内存映射文件和之前说的 标准IO操作最大的不同之处就在于它虽然最终也是要从磁盘读取数据,但是它并不需要将数据读取到OS内核缓冲区,而是直接将进程的用户私有地址空间中的一 部分区域与文件对象建立起映射关系,就好像直接从内存中读、写文件一样,速度当然快了。为了说清楚这个,我们以 Linux操作系统为例子,看下图:
image.png

此图为 Linux 2.X 中的进程虚拟存储器,即进程的虚拟地址空间,如果你的机子是 32 位,那么就有 2^32 = 4G的虚拟地址空间,我们可以看到图中有一块区域: “Memory mapped region for shared libraries” ,这段区域就是在内存映射文件的时候将某一段的虚拟地址和文件对象的某一部分建立起映射关系,此时并没有拷贝数据到内存中去,而是当进程代码第一次引用这 段代码内的虚拟地址时,触发了缺页异常,这时候OS根据映射关系直接将文件的相关部分数据拷贝到进程的用户私有空间中去,当有操作第N页数据的时候重复这样的OS页面调度程序操作。注意啦,原来内存映射文件的效率比标准IO高的重要原因就是因为少了把数据拷贝到OS内核缓冲区这一步

  • java中提供了3种内存映射模式,即:只读(readonly)、读写(read_write)、专用(private) ,

对于 只读模式来说,如果程序试图进行写操作,则会抛出ReadOnlyBufferException异 常;第二种的读写模式表明了通过内存映射文件的方式写或修改文件内容的话是会立刻反映到磁盘文件中去的,别的进程如果共享了同一个映射文件,那么也会立即 看到变化!而不是像标准IO那样每个进程有各自的内核缓冲区,比如JAVA代码中,没有执行
IO输出流的 flush() 或者 close() 操作,那么对文件的修改不会更新到磁盘去,除非进程运行结束;最后一种专用模式采用的是OS的“写时拷贝”原则,即在没有发生写操作的情况下,多个进程之 间都是共享文件的同一块物理内存(进程各自的虚拟地址指向同一片物理地址),一旦某个进程进行写操作,那么将会把受影响的文件数据单独拷贝一份到进程的私 有缓冲区中,不会反映到物理文件中去。

四、DirectBuffer相比HeapBuffer优势?(比HeapBuffer少了一次内存拷贝),注意下方参考中的,知乎专栏。

  • 一个Java进程相对于操作系统而言,肯定是一个用户进程。所以Java就有了这3G的使用权。JVM想使用这些内存的时候,就要使用一个叫 malloc 的方法去问操作系统要(其实中间还隔了一个C runtime,我们不去管这个细节,只把malloc往下都看成是操作系统的功能,并不会带来太大的问题)。
image.png

DirectBuffer的GC优点

  • 直接内存不受 GC(新生代的Minor GC)影响,只有当执行老年代的 Full GC时候才会顺便回收直接内存!
  • DirectBuffer当然还有一个直观的优点,不被GC管理,所以发生GC的时候,整理内存的压力就会小。当然,我后面也会讲,它并不是完全不被GC管理,它还是能被回收的,但是在GC平常整理内存的时候确实是不会去管它的。

五、“零拷贝”(FileChannel的transferTo和transferFrom)

Java NIO中提供的FileChannel拥有transferTo和transferFrom两个方法,可直接把FileChannel中的数据拷贝到另外一个Channel,或者直接把另外一个Channel中的数据拷贝到FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。在操作系统支持的情况下,通过该方法传输数据并不需要将源数据从内核态拷贝到用户态,再从用户态拷贝到目标通道的内核态,同时也避免了两次用户态和内核态间的上下文切换,也即使用了“零拷贝”,所以其性能一般高于Java IO中提供的方法。

参考

http://www.aichengxu.com/java/888073.htm
https://zhuanlan.zhihu.com/p/27625923

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容