Java NIO-Buffer

Buffer和Channel總是成對出現(xiàn),在Java NIO中Buffer用于和NIO通道進(jìn)行交互,數(shù)據(jù)總是從Chanel中讀入緩沖區(qū),然后在從緩沖區(qū)寫入Channel中匕积。

緩沖區(qū)本質(zhì)上是一塊物理上連續(xù)分配的內(nèi)存區(qū)域,這塊內(nèi)存區(qū)域被包裝成NIO Buffer對象榜跌,并提供了一組方法闪唆,用于方便的訪問該內(nèi)存區(qū)域。Buffer映射操作能夠直接操作底層平臺的資源钓葫。這些操作節(jié)省了在不同地址空間中復(fù)制數(shù)據(jù)的開銷——這在現(xiàn)代計算機(jī)體系結(jié)構(gòu)中是開銷很大的操作(相比于Java 面向流的IO)悄蕾。

1- 常用的Buffer類型


可以通過char,short础浮,int帆调,long,float 或 double類型來操作緩沖區(qū)中的字節(jié)豆同, MappedByteBuffer贷帮,用于表示內(nèi)存映射文件。

2- Buffer的分配

在使用Buffer之前需要先分配指定大小的內(nèi)存區(qū)域诱告。分配的緩沖區(qū)大小是定長的,不可以擴(kuò)展容量。并將分配的緩沖區(qū)元素都置為0精居。

  1. 不同類型Buffer的分配

每個Buffer類型的數(shù)據(jù)都有一個allocate的靜態(tài)方法來分配指定類型大小的緩沖數(shù)據(jù)區(qū)域锄禽,而且這些Buffer類型都是抽象類,不可實(shí)例化(但是可以使用類的靜態(tài)方法)靴姿。

  1. ByteBuffer不同內(nèi)存區(qū)域的Buffer分配
  • 在Java堆上分配內(nèi)存沃但,HeapByteBuffer是NIO的包內(nèi)訪問權(quán)限類,包外不可獲取佛吓,方法返回向上轉(zhuǎn)型為ByteBuffer宵晚,其他Buffer類也都有這個方法,分配的是堆上內(nèi)存區(qū)域(新建了一個byte數(shù)組)维雇。

  • 在堆外內(nèi)存上分配緩沖區(qū)淤刃,只有ByteBuffer可以分配堆外緩沖區(qū)。

    堆外內(nèi)存使用unsafe.allocateMemory方法分配

    但是可以先分配ByteBuffer的堆外內(nèi)存吱型,然后轉(zhuǎn)換為其他類型的來訪問逸贾。

    還有堆外分配不一定能分配成功,需要進(jìn)行判斷
  1. 堆上(HeapByteBuffer) VS 堆外(DirectByteBuffer)
  • 分配和銷毀堆外直接內(nèi)存緩沖區(qū)通常要比分配和銷毀堆上緩沖區(qū)消耗更多的系統(tǒng)資源津滞。
  • DirectByteBuffer比HeapByteBuffer讀寫性能更高铝侵,可以提高網(wǎng)絡(luò)交互的速度:HeapByteBuffer會發(fā)生頻繁的直接內(nèi)存和JVM堆內(nèi)存之間的相互的拷貝,比如flush數(shù)據(jù)到遠(yuǎn)程的時候触徐,會將JVM內(nèi)存拷貝到直接內(nèi)存然后才會進(jìn)行數(shù)據(jù)發(fā)送的工作咪鲜;而DirectByteBuffer是對直接內(nèi)存的保證所以會省去內(nèi)存拷貝的過程,這在計算操作系統(tǒng)中節(jié)省可觀的性能消耗撞鹉。
  • 基于NIO的開源web框架如Netty疟丙,Nginx都基于DirectByteBuffer提高了整體讀寫性能。如Netty的ByteBuf完成了對ByteBuffer的包裝和拓展孔祸,需要注意的是在NIO網(wǎng)絡(luò)編程時只有ByteBuffer能和Channel進(jìn)行讀寫交互隆敢,ByteBuf在于Channel進(jìn)行交互時也是轉(zhuǎn)換成了ByteBuffer之后再與Channel交互。
  • 由于分配DirectByteBuffer比較消耗系統(tǒng)資源崔慧,但是又能提高讀寫性能拂蝎,Netty的做法是分配DirectByteBuffer后自己根據(jù)引用計數(shù)做內(nèi)存回收,重新復(fù)用DirectByteBuffer惶室,而不是直接釋放掉分配好的內(nèi)存温自。
  1. DirectByteBuffer回收管理(程序員完全控制管理)

直接內(nèi)存的釋放是GC(full gc,調(diào)用System.gc())自動回收來控制的皇钞,并不由程序員控制悼泌,沒有類似的close或者free等顯示釋放內(nèi)存空間的方法,但是如何才能有程序員完全掌握回收的控制權(quán)呢夹界?DirectByteBuffer有一個Cleaner域用于內(nèi)存釋放馆里,給了程序猿一線生機(jī)。

  1. 設(shè)置JVM 參數(shù)DisableExplicitGC ,禁用full gc(這樣DirectByteBuffer就不會被系統(tǒng)回收了) 鸠踪,嚴(yán)重警告:禁用full gc丙者,需要嚴(yán)格的測試,存在內(nèi)存泄露的風(fēng)險营密,必要進(jìn)行堆外內(nèi)存管理(其實(shí)Netty就是這么干的)
  2. DirectByteBuffer構(gòu)造函數(shù)會新建一個Deallocator類來初始化這個Cleaner域
  1. Deallocator是DirectByteBuffer靜態(tài)內(nèi)部類械媒,含有一個run方法,方法內(nèi)部使用unsafe.freeMemory釋放分配的直接內(nèi)存空間


  2. Cleaner類有一個方法clean方法评汰,調(diào)用的是傳進(jìn)去的Deallocator對象的run方法纷捞,可以用來釋放分配的堆外直接內(nèi)存。


  3. 代碼示例

    DirectByteBuffer實(shí)現(xiàn)了DirectBuffer接口被去,DirectByteBuffer是default訪問權(quán)限主儡,但是DirectBuffer是public,如果不是出于特殊考慮建議不要通過DirectBuffer直接操作DirectByteBuffer编振,容易造成安全隱患(這也是DirectByteBuffer定義為default訪問權(quán)限的原因)

3- Buffer的使用

Buffer的使用一般搭配Channel

  1. 將數(shù)據(jù)讀到Buffer中
  1. 將Buffer中數(shù)據(jù)讀出

get方法也可以獲取指定位置的數(shù)據(jù)

  1. position(位置)缀辩、capacity(容量)、limit(限制)

position指的是當(dāng)前在緩沖數(shù)組中的位置踪央;capacity指的是緩沖數(shù)組的大小臀玄,在創(chuàng)建時指定,代表著最大可存儲的數(shù)據(jù)長度畅蹂;limit指的是有效數(shù)據(jù)的長度健无,limit小于等于capacity。

  1. 讀寫模式轉(zhuǎn)換時液斜,PCL的變化
  • 新建:position=0累贤;limit = capacity
  • 讀入數(shù)據(jù)大小5byte:position = 5;limit=capacity
  • flip(寫->讀)模式轉(zhuǎn)換:position=0少漆;limit=轉(zhuǎn)換前position位置
  • clear方法:和新建一樣
  • rewind方法:position=0臼膏;limit=保持不變

flip方法用于寫->讀轉(zhuǎn)換,clear方法用于重置緩沖數(shù)組等待下一次將數(shù)據(jù)寫入緩沖數(shù)組示损,rewind方法用于重讀數(shù)組渗磅,需要注意的是這些只是position、limit 的位置在發(fā)生變化检访,緩沖數(shù)組的數(shù)據(jù)并沒有被清除始鱼,只有下次寫入才能將原來的數(shù)據(jù)覆蓋。當(dāng)將緩沖數(shù)組數(shù)據(jù)讀出時脆贵,只會讀出position-limit范圍內(nèi)的數(shù)據(jù)(有效數(shù)據(jù))医清,而不會讀取limit-capacity之間的數(shù)據(jù)(上次寫入時遺留的數(shù)據(jù),等待被覆蓋)卖氨。
其他的方法還有mark()與reset()方法会烙,通過調(diào)用Buffer.mark()方法负懦,可以標(biāo)記Buffer中的一個特定position。之后可以通過調(diào)用Buffer.reset()方法恢復(fù)到這個position持搜。這樣就提供了在一個緩沖數(shù)組中反復(fù)遍歷操作讀入數(shù)據(jù)的便利和靈活性密似,而不像面向流的IO不能操作當(dāng)前位置的前一個字節(jié)。

參考
http://ifeve.com/buffers/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葫盼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子村斟,更是在濱河造成了極大的恐慌贫导,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟆盹,死亡現(xiàn)場離奇詭異孩灯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逾滥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門峰档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寨昙,你說我怎么就攤上這事讥巡。” “怎么了舔哪?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵欢顷,是天一觀的道長。 經(jīng)常有香客問我捉蚤,道長抬驴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任缆巧,我火速辦了婚禮布持,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陕悬。我一直安慰自己题暖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布墩莫。 她就那樣靜靜地躺著芙委,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狂秦。 梳的紋絲不亂的頭發(fā)上灌侣,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音裂问,去河邊找鬼侧啼。 笑死牛柒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的痊乾。 我是一名探鬼主播皮壁,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哪审!你這毒婦竟也來了蛾魄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤湿滓,失蹤者是張志新(化名)和其女友劉穎滴须,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叽奥,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扔水,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了朝氓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔市。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赵哲,靈堂內(nèi)的尸體忽然破棺而出待德,到底是詐尸還是另有隱情,我是刑警寧澤誓竿,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布磅网,位于F島的核電站,受9級特大地震影響筷屡,放射性物質(zhì)發(fā)生泄漏涧偷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一毙死、第九天 我趴在偏房一處隱蔽的房頂上張望燎潮。 院中可真熱鬧,春花似錦扼倘、人聲如沸确封。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爪喘。三九已至,卻和暖如春纠拔,著一層夾襖步出監(jiān)牢的瞬間秉剑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工稠诲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侦鹏,地道東北人诡曙。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像略水,于是被迫代替她去往敵國和親价卤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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