3.NIO直接缓冲区与非直接缓冲区

非直接缓冲区,缓冲区建立在JVM内存中,实际读写数据时,需要在OS和JVM之间进行数据拷贝,如下图:

非直接缓冲区

为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色

直接缓冲区,缓冲区建立在受操作系统管理的物理内存中,OS和JVM直接通过这块物理内存进行交互,没有了中间的拷贝环节,如下图:

直接缓冲区

所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件RAM)内存地址。这样做好处颇多,总结起来可分为两大类: 1. 一个以上的虚拟地址可指向同一个物理内存地址。 2. 虚拟内存空间可大于实际可用的硬件内存。
前一节提到,设备控制器不能通过 DMA 直接存储到用户空间,但通过利用上面提到的第一项,则可以达到相同效果。把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区(见图1-3)。

优点:速度更快,效率更高
缺点:①创建直接缓冲区将会有更多消耗②数据进入直接缓冲区后,后续写入磁盘等操作就完全由操作系统决定了,不受我们控制
什么时候用:缓冲区要长时间使用(数据本身需要长时间在内存
OR 缓冲区复用率很高),或者大数据量的操作(大文件才能体现出速度优势)。
如何使用

  1. 通过ByteBuffer.allocateDirect(),创建直接缓冲区
  2. 通过内存映射文件的方式
  3. 使用通道直接传输

下面以2G大小的文件,对比一下


非直接缓冲区完成文件复制

public void testChannel() throws Exception{
        long begin = System.currentTimeMillis();
        
        FileInputStream fis = new FileInputStream("1.zip");
        FileOutputStream fos = new FileOutputStream("2.zip");
        
        //获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        
        //创建Buffer
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        
        //将数据写入byteBuffer
        while(inChannel.read(byteBuffer) != -1){
            //转换模式
            byteBuffer.flip();
            //将byteBuffer中的数据读取到outChannel
            //注意这里的byteBuffer是有position limit属性的,write时候只会写position->limit之间的数据
            //所以,即使clear()并未真正清空buffer,这里也不会把上次的数据写入的
            outChannel.write(byteBuffer);
            //清空byteBuffer
            byteBuffer.clear();
        }
        
        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();
        
        System.out.println(System.currentTimeMillis() - begin);
    }

直接缓冲区完成文件复制
与上述代码一样,只是将创建缓冲区的方式改成ByteBuffer.allocateDirect()
内存映射文件方式
①原视频中直接将1G左右的文件一次性通过mapBuffer进行传输,这样比较的结果可能不准确,因为非直接缓冲区一次只读取1024个字节,所以我这里也改成了一次读1024个字节
②我用的是2G的文件,本来想试试一次读2G和多次读差别多大,结果直接报堆溢出,不是都直接操作物理内存了嘛,咋还跟JVM堆有关,这可能涉及到更深的内容,而且NIO重点也不在这块儿,所以这里就暂将此问题挂起,不深入研究了

public void testChannel2() throws Exception{
    long begin = System.currentTimeMillis();
    
    //另一种方式获取Channel
    FileChannel inChannel = FileChannel.open(Paths.get("1.zip"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("3.zip"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    
    //内存映射文件
    MappedByteBuffer inMapBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
    MappedByteBuffer outMapBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
    
    //直接对映射缓冲区进行读写,不需要ByteBuffer
    //这里不能直接传2g,否则会报错堆溢出,要分开传
    byte [] tempBytes = new byte[1024];
        while(inMapBuffer.hasRemaining()){
            int shouldReadLength = inMapBuffer.remaining() > 1024?1024:inMapBuffer.remaining();
            //写入bytes
            inMapBuffer.get(tempBytes,0,shouldReadLength);
            //从bytes读出到outMapBuffer
            outMapBuffer.put(tempBytes,0,shouldReadLength);
        }
        
        
    outChannel.close();
    inChannel.close();
    
    System.out.println(System.currentTimeMillis() - begin);
}

通道直接传输
只有FileChannel有这个方式,不需要借助缓冲区

public void testChannel3() throws Exception{
    long begin = System.currentTimeMillis();

    FileChannel inChannel = FileChannel.open(Paths.get("1.zip"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("3.zip"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    
    //这里传2g的也不会报堆溢出
    inChannel.transferTo(0, inChannel.size(), outChannel);
    //outChannel.transferFrom(inChannel, 0, inChannel.size());
    
    outChannel.close();
    inChannel.close();

    System.out.println(System.currentTimeMillis() - begin);
}

到这里应该能明白NIO与IO的第一个区别了吧:

NIO是面向缓冲区,基于通道进行IO操作,能以更加高效的方式进行文件的读取操作

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

推荐阅读更多精彩内容

  • 操作系统概论 操作系统的概念 操作系统是指控制和管理计算机的软硬件资源,并合理的组织调度计算机的工作和资源的分配,...
    野狗子嗷嗷嗷阅读 11,920评论 3 34
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,234评论 11 349
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,798评论 0 27
  • 站在摩托车上发信手拿手机双肩靠车双手拿手机不停点击看到一个老人带老人步履蹒跚慢前行表情严肃很认真,身边不断有车过,...
    飞的更好阅读 133评论 0 0
  • 生活坏到一定程度一定会好起来 因为它无法更坏 努力过了再知道有些事情 坚持坚持...
    C1992BX阅读 157评论 0 2