淺談ByteBuffer與ByteBuf

I/O

作為開(kāi)發(fā)者横辆,I/O是一定會(huì)遇到的。以常見(jiàn)的文件操作為例困肩,原生的java代碼如下:

// 基本字節(jié)流一次讀寫(xiě)一個(gè)字節(jié)數(shù)組
public static void method2(String srcString, String destString)
        throws IOException {
    FileInputStream fis = new FileInputStream(srcString);
    FileOutputStream fos = new FileOutputStream(destString);

    byte[] bys = new byte[1024];
    int len = 0;
    while ((len = fis.read(bys)) != -1) {
        fos.write(bys, 0, len);
    }

    fos.close();
    fis.close();
}

用FileInputStream能完成基本的功能脆侮,但是會(huì)有一個(gè)問(wèn)題就是,性能上還有優(yōu)化的空間潭枣。
問(wèn)題出在哪里幻捏?FileInputStream.read和FileOutputStream.write這兩個(gè)方法,每次執(zhí)行都會(huì)進(jìn)行I/O操作蚣抗,由于I/O操作是比較好資源的瓮下,頻繁的操作必然導(dǎo)致性能上的問(wèn)題。

為什么使用Buffer

上面我們講到FileInputStream.read和FileOutputStream.write會(huì)頻繁地進(jìn)行I/O操作锭魔。為了解決這個(gè)阻塞的問(wèn)題路呜,引入了Buffer這個(gè)概念。
Buffer做了什么事情呢漠秋?有了Buffer以后抵屿,每次的讀取,java會(huì)讀取更多的數(shù)據(jù)到緩沖區(qū)里搂抒,下次再調(diào)用read方法時(shí),如果緩沖區(qū)里的數(shù)據(jù)夠了求晶,就直接返回?cái)?shù)據(jù)芳杏,不用再執(zhí)行I/O操作。寫(xiě)入也是如此蚜锨,通過(guò)這種方式亚再,化零為整晨抡,減低了I/O操作的頻率,提升效率如捅。

buffer 的主要目的進(jìn)行流量整形调煎,把突發(fā)的大數(shù)量較小規(guī)模的 I/O 整理成平穩(wěn)的小數(shù)量較大規(guī)模的 I/O,以減少響應(yīng)次數(shù)(比如從網(wǎng)上下電影悲关,你不能下一點(diǎn)點(diǎn)數(shù)據(jù)就寫(xiě)一下硬盤(pán)娄柳,而是積攢一定量的數(shù)據(jù)以后一整塊一起寫(xiě),不然硬盤(pán)都要被你玩壞了)秫筏。

// 高效字節(jié)流一次讀寫(xiě)一個(gè)字節(jié)數(shù)組:
public static void method4(String srcString, String destString)
        throws IOException {
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
            srcString));
    //為什么不傳遞一個(gè)具體的文件或者文件路徑挎挖,而是傳遞一個(gè)OutputStream對(duì)象?
    //因?yàn)樽止?jié)緩沖區(qū)流僅僅提供緩沖區(qū),為高效而設(shè)計(jì)的崔涂。真正的讀寫(xiě)操作還是基本的流對(duì)象實(shí)現(xiàn)墓造。

    //構(gòu)造方法可以指定緩沖區(qū)的大小锚烦,但是我們一般不用涮俄,默認(rèn)緩沖區(qū)大小就夠了
    BufferedOutputStream bos = new BufferedOutputStream(
            new FileOutputStream(destString));

    byte[] bys = new byte[1024];
    int len = 0;
    while ((len = bis.read(bys)) != -1) {
        bos.write(bys, 0, len);
    }

    bos.close();
    bis.close();
}

ByteBuffer

上面介紹了BufferedOutputStream尸闸,在網(wǎng)絡(luò)I/O方面,用的最多的就是ByteBuffer了苞尝。ByteBuffer的使用方式如下:

ByteBuffer byteBuffer = ByteBuffer.allocate(88);
String value = "netty";
byteBuffer.put(value.getBytes());
byteBuffer.flip();
byte[] valueArray = new byte[byteBuffer.remaining()];
byteBuffer.get(valueArray);
String decodeVaule = new String(valueArray);

但是JDK自帶的ByteBuffer并不足夠完美宦芦,它有以下缺陷:

  • ByteBuffer長(zhǎng)度固定,一旦分配完成抡砂,它的容量不能動(dòng)態(tài)擴(kuò)展和收縮恬涧,當(dāng)需要編碼的POJO對(duì)象大于ByteBuffer的容量時(shí),會(huì)發(fā)生索引越界異常丑搔;
  • ByteBuffer只有一個(gè)標(biāo)識(shí)位控的指針position,讀寫(xiě)的時(shí)候需要手工調(diào)用flip()和rewind()等提揍,使用者必須小心謹(jǐn)慎地處理這些API,否則很容易導(dǎo)致程序處理失敗顽冶;
  • ByteBuffer的API功能有限售碳,一些高級(jí)和實(shí)用的特性它不支持,需要使用者自己編程實(shí)現(xiàn)间景。

ByteBuf

大名鼎鼎的通信框架netty為了解決ByteBuffer的缺陷艺智,重寫(xiě)了一個(gè)新的數(shù)據(jù)接口ByteBuf。
與ByteBuffer相比封拧,ByteBuf提供了兩個(gè)指針,分別記錄讀和寫(xiě)的操作位置曹铃。
初始分配的ByteBuf:


初始分配的ByteBuf

寫(xiě)入N個(gè)字節(jié)之后的ByteBuf:


寫(xiě)入N個(gè)字節(jié)之后的ByteBuf

讀取M(<N)個(gè)字節(jié)之后的ByteBuf:
讀取M(<N)個(gè)字節(jié)之后的ByteBuf

調(diào)用discardReadBytes操作之后的ByteBuf:
調(diào)用discardReadBytes操作之后的ByteBuf

調(diào)用clear操作之后的ByteBuf:


調(diào)用clear操作之后的ByteBuf

字節(jié)緩沖區(qū)

netty為了進(jìn)一步優(yōu)化提升性能,支持了堆外緩沖區(qū)评甜。

屬性 Heap buffer Direct Buffer
位置 堆內(nèi) 堆外
內(nèi)存分配速度
內(nèi)粗能回收速度
Socket的I/O讀寫(xiě) 需要額外的內(nèi)存復(fù)制 不需要額外的內(nèi)存復(fù)制

netty官方有一句描述了使用直接緩沖區(qū)的風(fēng)險(xiǎn)仔涩。

allocating many short-lived direct NIO buffers often causes an OutOfMemoryError.

為了更高效地使用堆外緩沖區(qū)红柱,netty通過(guò)內(nèi)存池和引用計(jì)數(shù)很好地繞開(kāi)了Direct Buffer的劣勢(shì)蓖乘,發(fā)揚(yáng)了它的優(yōu)勢(shì)。

關(guān)于zero copy

Netty的零拷貝體現(xiàn)在三個(gè)方面:

  • Direct Buffers
  • Composite Buffers
  • FileChannel.transferTo

參考資料

Cache 和 Buffer 都是緩存嘉抒,主要區(qū)別是什么些侍?
IO、NIO岗宣、Netty
Using as a generic library
Netty中的零拷貝

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耗式,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子彪见,更是在濱河造成了極大的恐慌娱挨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酵镜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡誉裆,警方通過(guò)查閱死者的電腦和手機(jī)缸濒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)庇配,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捞慌,你說(shuō)我怎么就攤上這事啸澡。” “怎么了嗅虏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵皮服,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我硫眯,道長(zhǎng)择同,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任裹纳,我火速辦了婚禮归斤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘她我。我一直安慰自己,他們只是感情好番舆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布恨狈。 她就那樣靜靜地躺著,像睡著了一般返奉。 火紅的嫁衣襯著肌膚如雪吗氏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天污尉,我揣著相機(jī)與錄音往产,去河邊找鬼。 笑死锐朴,一個(gè)胖子當(dāng)著我的面吹牛奠宜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼滴肿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼佃迄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起堆缘,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤普碎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缀皱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡表箭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年免钻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了崔拥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姆怪,死狀恐怖澡绩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溪掀,我是刑警寧澤步鉴,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布氛琢,位于F島的核電站,受9級(jí)特大地震影響阳似,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俏讹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一畜吊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殉疼,春花似錦、人聲如沸驱证。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伙单。三九已至哈肖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間淤井,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工游两, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漩绵,地道東北人止吐。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瘩燥,于是被迫代替她去往敵國(guó)和親蕴忆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容