Java IO與NIO淺談

一鸭丛、傳統(tǒng)IO模式下的文件讀取
傳統(tǒng)的文件IO操作都是調(diào)用OS提供的底層標(biāo)準(zhǔn)IO操作讀取函數(shù)read()方淤、write();然后調(diào)用此函數(shù)的進(jìn)程(即java進(jìn)程)由java用戶態(tài)切換至內(nèi)核態(tài)。然后OS內(nèi)核代碼負(fù)責(zé)將相應(yīng)的文件讀取到內(nèi)核IO緩存之中源请,然后再把數(shù)據(jù)從內(nèi)核IO緩存之中拷貝到進(jìn)程相應(yīng)的私有地址空間中富稻。則完成一次IO操作筒狠;

Q&A:

Q:為什么要搞一個(gè)內(nèi)核IO緩存舔庶,將原本拷貝一次數(shù)據(jù)的操作整兩次抛蚁?

A:因?yàn)闉榱藴p少磁盤的IO操作,提升性能惕橙;因?yàn)槲覀兊某绦蚓哂芯植啃郧扑Γ此^的局部性原理,在這里是空間局部性吕漂;即我們?cè)L問文件中的一段數(shù)據(jù)亲配,接下來可能還會(huì)訪問接下去的一段數(shù)據(jù)尘应,而磁盤的IO操作相對(duì)于內(nèi)存慢了好幾個(gè)數(shù)量級(jí)惶凝,所以O(shè)S根據(jù)局部性原理會(huì)在read()時(shí)吼虎,會(huì)預(yù)讀更多的文件數(shù)據(jù)存放在內(nèi)核IO緩存中,當(dāng)訪問的文件數(shù)據(jù)在內(nèi)核IO緩沖區(qū)之中時(shí)直接將數(shù)據(jù)拷貝到進(jìn)程私有內(nèi)存地址之中(也有可能經(jīng)過了native堆中轉(zhuǎn)苍鲜,因?yàn)檫@些函數(shù)都是聲明為native本地平臺(tái)相關(guān))思灰。避免低效率的磁盤IO讀取。

Q:既然有內(nèi)核IO緩存混滔,那java為什么還提供BufferedInputStream對(duì)象洒疚?

A:因?yàn)閺膬?nèi)核IO緩存中拷貝到進(jìn)程私有空間數(shù)據(jù)系統(tǒng)調(diào)用,而系統(tǒng)調(diào)用相對(duì)來數(shù)代價(jià)是比較高的坯屿,需要從java用戶態(tài)和內(nèi)核態(tài)的上下文切換油湖。

二,java NIO讀取模式

  1. java內(nèi)存映射文件讀取模式

簡介:將進(jìn)程的用戶私有空間的一部分區(qū)域與文件對(duì)象建立映射關(guān)系领跛。并不需要將文件拷貝到內(nèi)核IO緩存之中乏德,類似于直接從內(nèi)存中讀取數(shù)據(jù),這樣速度當(dāng)然快吠昭。

java之中針對(duì)于內(nèi)存映射提供了三種模式:只讀(readonly)喊括、讀寫(read_write)、專有(private)矢棚;

  • readonly:異常:針對(duì)只讀模式郑什,如果嘗試寫操作,則ReadonlyBufferException

  • read_write:可以進(jìn)行讀寫操作蒲肋,表明在通過內(nèi)存文件映射的方式寫或者修改能直接反應(yīng)到文件對(duì)象中去蘑拯; 如果其他進(jìn)程共享了文件對(duì)象,那個(gè)能直接看到變化兜粘;不像標(biāo)準(zhǔn)IO模式强胰,每個(gè)進(jìn)程都有自己的內(nèi)核緩沖;類似于java進(jìn)程妹沙,必須要flash()或者close()操作才能將修改更正到磁盤文件對(duì)象中去偶洋;

  • write:采用OS“寫時(shí)拷貝”原則,在沒有進(jìn)程寫操作的時(shí)候距糖,多個(gè)進(jìn)程之間都是共享文件的同一快物理內(nèi)存(即各個(gè)進(jìn)程的虛擬地址指向同一片物理地址)玄窝;一旦某個(gè)進(jìn)程進(jìn)行寫操作,則會(huì)將受影響的文件數(shù)據(jù)單獨(dú)拷貝一份到進(jìn)程的私有緩沖區(qū)中去悍引,而不會(huì)反映到物理文件中去恩脂;

備注:

  • java中對(duì)于進(jìn)程單次文件IO限制Integer.MAX_VALUE,即2G左右趣斤,但是可以通過分次映射文件不同部分來達(dá)到操作整個(gè)文件的目的俩块。


    image.png
  • java內(nèi)存映射文件數(shù)據(jù)JVM直接緩沖區(qū), 還可以通過 ByteBuffer.allocateDirect() ,即DirectMemory的方式來創(chuàng)建直接緩沖區(qū)玉凯。他們相比基礎(chǔ)的 IO操作來說就是少了中間緩沖區(qū)的數(shù)據(jù)拷貝開銷势腮。同時(shí)他們屬于JVM堆外內(nèi)存,不受JVM堆內(nèi)存大小的限制漫仆。
package com.seriousty.practice.memory;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.nio.ByteBuffer;

import java.nio.MappedByteBuffer;

import java.nio.channels.FileChannel;

/**

* Created with IntelliJ IDEA

* Created By seriousty

* Date: 2018/8/24

* Time: 15:05

* BOLG: [https://github.com/seriousty](https://github.com/seriousty)

* Description:

*/

public class IOTest {

private static int MAX_SIZE = 1024;

/**

*NIO操作:

*/

public static void main(String[] args) {

File file=new File("[h://iofile.pdf](file:///h://iofile.pdf)");

FileInputStream fis = null;

try {

fis = new FileInputStream(file);

FileChannel channel = fis.getChannel();

MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());//time consuming:39

byte[] bytes=new byte[1024];

int length = (int) file.length();

long start = System.currentTimeMillis();

for (int i = 0; i <length ; i+=1024) {

if(length-i>MAX_SIZE){

map.get(bytes);

}else{

map.get(new byte[length-i]);

}

}

long end = System.currentTimeMillis();

System.out.println("time consuming:"+(end-start));//time consuming:41

} catch (Exception e) {

e.printStackTrace();

}

}

}
普通IO模式:

public static void main(String[] args) {

File file = new File("[h://iofile.pdf](file:///h://iofile.pdf)");

try {

FileInputStream fis = new FileInputStream(file);

FileChannel channel = fis.getChannel();

 ByteBuffer allocate = ByteBuffer.allocate(1024);

//ByteBuffer allocate = ByteBuffer.allocateDirect((int) channel.size());

long start = System.currentTimeMillis();

while (channel.read(allocate) != -1) {

allocate.flip();

allocate.clear();

}

long end = System.currentTimeMillis();

System.out.println("time consuming:"+(end-start));//time consuming:1014

} catch (Exception e) {

e.printStackTrace();

}

}

2,直接內(nèi)存(Directed Memory)

  • 直接內(nèi)存大小默認(rèn)等同于JVM堆大小 -Xmx:JVM堆大猩诱;

  • 但是直接內(nèi)存大小不受JVM堆的限制 由JVM參數(shù) -XX:MaxDirectMemorySize 單獨(dú)配置

給直接內(nèi)存設(shè)置最大內(nèi)存大小

image.png

給直接內(nèi)存分配內(nèi)存


image.png

結(jié)論:

執(zhí)行了多次Full GC,沒有執(zhí)行GC(不受新生代的影響)盲厌,只有當(dāng)回收老年代的時(shí)候再回順便回收直接內(nèi)存署照?why,因?yàn)橹苯觾?nèi)存是通過DirectByteBuffer對(duì)象來引用的吗浩,所以當(dāng)DirectByteBuffer對(duì)象由新生代進(jìn)入老年代后建芙,在老年代觸發(fā)了Full GC.

總結(jié):

NIO中的DirectMemory和內(nèi)存映射文件都是直接內(nèi)存緩沖,但是DirectMemory能通過JVM -XX:+Xmx和-XX:MaxDirectMemorySize參數(shù)來控制懂扼,內(nèi)存映射文件沒有JVM參數(shù)可以控制岁钓;

兩者內(nèi)存分配位置:

DirectMemory:在java進(jìn)程中的native堆中分配,不受young gc控制微王,避免了在java堆和native堆中copy屡限,提高文件傳輸?shù)男阅?/p>

放java向外進(jìn)行數(shù)據(jù)傳輸時(shí),需要先將數(shù)據(jù)從java堆拷貝到native堆之中炕倘。

內(nèi)存映射文件:沒有經(jīng)過native堆钧大,由java進(jìn)程私有內(nèi)存空間一部分于文件對(duì)象建立關(guān)聯(lián)的映射關(guān)系。所以也不會(huì)受young gc影響罩旋。

資料引用:

1) Heap memory: memory within the JVM process that is managed by the JVM to represent Java objects
2) Native memory/Off-heap: is memory allocated within the processes address space that is not within the heap.
3) Direct memory: is similar to native, but also implies that an underlying buffer within the hardware is being shared. For example buffer within the network adapter or graphics display. The goal here is to reduce the number of times the same bytes is being copied about in memory.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啊央,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涨醋,更是在濱河造成了極大的恐慌瓜饥,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浴骂,死亡現(xiàn)場(chǎng)離奇詭異乓土,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)溯警,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門趣苏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梯轻,你說我怎么就攤上這事食磕。” “怎么了喳挑?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵彬伦,是天一觀的道長滔悉。 經(jīng)常有香客問我,道長单绑,這世上最難降的妖魔是什么回官? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮询张,結(jié)果婚禮上孙乖,老公的妹妹穿的比我還像新娘浙炼。我一直安慰自己份氧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布弯屈。 她就那樣靜靜地躺著蜗帜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪资厉。 梳的紋絲不亂的頭發(fā)上厅缺,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音宴偿,去河邊找鬼湘捎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窄刘,可吹牛的內(nèi)容都是我干的窥妇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼娩践,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼活翩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起翻伺,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤材泄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吨岭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拉宗,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年辣辫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了簿废。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡络它,死狀恐怖族檬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情化戳,我是刑警寧澤单料,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布埋凯,位于F島的核電站,受9級(jí)特大地震影響扫尖,放射性物質(zhì)發(fā)生泄漏白对。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一换怖、第九天 我趴在偏房一處隱蔽的房頂上張望甩恼。 院中可真熱鬧,春花似錦沉颂、人聲如沸条摸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钉蒲。三九已至,卻和暖如春彻坛,著一層夾襖步出監(jiān)牢的瞬間顷啼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工昌屉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钙蒙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓间驮,卻偏偏與公主長得像躬厌,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜻牢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348