国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

使用堆內內存HeapByteBuffer的注意事項

存儲 存儲軟件
本文介紹了在文件 IO 中使用 HeapByteBuffer 的注意事項,需要考慮到 FileChannel 內部的復制問題,意識到這一過程會有堆外內存的復制開銷。

[[427434]]

本文轉載自微信公眾號「Kirito的技術分享」,作者kiritomoe。轉載本文請聯系Kirito的技術分享公眾號。

前言

國慶假期一眨眼就過去了,本來在家躺平的很舒服,沒怎么肝云原生編程挑戰賽,傳送門:https://tianchi.aliyun.com/s/8bf1fe4ae2aea736e692c31c6952042d ,偏偏對手們假期開始卷起來了,眼看就要被人反超了,嚇得我趕緊繼續優化了。比賽大概還有一個月才結束,Kirito 的詳細方案也會在比賽結束后分享,這期間我會分享一些比賽中的一些通用優化或者細節知識點,例如本文就是這么一個例子。

趁著假期最后一天,分享一個很多人容易踩的一個坑:HeapByteBuffer 的使用問題。我們都知道 NIO 分裝了 ByteBuffer 接口,使得 filechannel 的文件 IO API 變得非常的簡單。ByteBuffer 主要有兩個實現類

  • HeapByteBuffer 堆內內存
  • DirectByteBuffer 堆外內存

按我的個人經驗,大多數情況,無論是讀操作還是寫操作,我都傾向于使用 DirectByteBuffer,主要是因為 HeapByteBuffer 在和 FileChannel 交互時,可能會有一些出乎大家意料的內部操作,也就是這篇文章的標題中提到的注意事項,這里先賣個關子。

先來看看這次比賽為什么要用到 HeapByteBuffer 呢?

原因一:賽題需要設計分級存儲,并且提供了 6G 堆內內存 + 2G 堆外內存,一個最直接的思路便是使用內存來存儲熱點數據,而內存存儲數據最方便的數據結構便是 ByteBuffer 了。

原因二:由于堆內 6G 遠大于堆外 2G,且 JVM 參數不能調整,所以要想利用好堆內富余的內存去做緩存,非 HeapByteBuffer 莫屬了。

可能有一些讀者并沒有關注賽題,我這里簡化一下前言,可以直接理解為:有一塊 2G 的 HeapByteBuffer 用于文件 IO,我們該如何利用。

HeapByteBuffer 的復制問題

廢話不多說,直接來看 HeapByteBuffer 的坑在哪兒。

使用代碼描述 HeapByteBuffer 的文件 IO 操作,大概率會寫出如下的代碼:

  1. public void readInOneThread() throws Exception { 
  2.     int bufferSize = 50 * 1024 * 1024; 
  3.     File file = new File("/essd"); 
  4.     FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel(); 
  5.     ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize); 
  6.     fileChannel.read(byteBuffer); 

上述的代碼,將文件中的數據緩存到了內存中,無論是賽題還是生產場景,這個行為通常都是多線程的,例如在云原生編程挑戰賽的評測下,有 40 個線程進行讀寫,如果按照線程維度進行緩存,每個線程分到 50M 用于內存緩存自然是沒有問題。

而如果你直接使用上述代碼,在評測中可能會直接得到內存溢出相關的異常。其實我在之前堆外內存泄漏的文章中也提到過這個問題,不過角度有所不同。原因很簡單,直接來看源碼。

FileChannel 使用的是 IOUtil 進行讀寫操作

  1. static int read(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException { 
  2.     if (var1.isReadOnly()) { 
  3.         throw new IllegalArgumentException("Read-only buffer"); 
  4.     } else if (var1 instanceof DirectBuffer) { 
  5.         return readIntoNativeBuffer(var0, var1, var2, var4); 
  6.     } else { 
  7.         ByteBuffer var5 = Util.getTemporaryDirectBuffer(var1.remaining()); 
  8.         int var7; 
  9.         try { 
  10.             int var6 = readIntoNativeBuffer(var0, var5, var2, var4); 
  11.             var5.flip(); 
  12.             if (var6 > 0) { 
  13.                 var1.put(var5); 
  14.             } 
  15.             var7 = var6; 
  16.         } finally { 
  17.             Util.offerFirstTemporaryDirectBuffer(var5); 
  18.         } 
  19.         return var7; 
  20.     } 

可以發現當使用 HeapByteBuffer 時,會走到下面這個分支

  1. Util.getTemporaryDirectBuffer(var1.remaining()); 

這個 Util 封裝了更為底層的一些 IO 邏輯

  1. package sun.nio.ch; 
  2. public class Util { 
  3.     private static ThreadLocal<Util.BufferCache> bufferCache; 
  4.      
  5.     public static ByteBuffer getTemporaryDirectBuffer(int var0) { 
  6.         if (isBufferTooLarge(var0)) { 
  7.             return ByteBuffer.allocateDirect(var0); 
  8.         } else { 
  9.             // FOUCS ON THIS LINE 
  10.             Util.BufferCache var1 = (Util.BufferCache)bufferCache.get(); 
  11.             ByteBuffer var2 = var1.get(var0); 
  12.             if (var2 != null) { 
  13.                 return var2; 
  14.             } else { 
  15.                 if (!var1.isEmpty()) { 
  16.                     var2 = var1.removeFirst(); 
  17.                     free(var2); 
  18.                 } 
  19.  
  20.                 return ByteBuffer.allocateDirect(var0); 
  21.             } 
  22.         } 
  23.     } 

isBufferTooLarge 這個方法會根據傳入 Buffer 的大小決定如何分配堆外內存,如果過大,直接分配大緩沖區;如果不是太大,會使用 bufferCache 這個 ThreadLocal 變量來進行緩存,從而復用(實際上這個數值非常大,幾乎不會走進直接分配堆外內存這個分支)。這么看來似乎發現了兩個不得了的結論:

使用 HeapByteBuffer 讀寫都會經過 DirectByteBuffer,寫入數據的流轉方式其實是:HeapByteBuffer -> DirectByteBuffer -> PageCache -> Disk,讀取數據的流轉方式正好相反。

使用 HeapByteBuffer 讀寫會申請一塊跟線程綁定的 DirectByteBuffer。這意味著,線程越多,臨時 DirectByteBuffer 就越會占用越多的空間。

根據這兩個結論,我們再回到賽題中,如果直接按照上述的方式進行讀寫,40 個線程每個都持有一個 50M 的堆內內存,同時又因為 IOUtil 的內部行為,額外分配了 40*50M 的堆外內存, 堆外內存在不經意間就被用光了!出現堆外內存溢出的異常也就不奇怪了。

為什么 HeapByteBuffer 在 IO 時需要復制到 DirectByteBuffer

這個我之前也介紹過,詳情可以參考我的一篇舊文:《一文探討堆外內存的監控與回收》??偨Y如下:

  • 為了方便 GC 的實現,DirectByteBuffer 指向的 native memory 是不受 GC 管轄的
  • HeapByteBuffer 背后使用的是 byte 數組,其占用的內存不一定是連續的,不太方便 JNI 方法的調用
  • 數組實現在不同 JVM 中可能會不同

解決方案

其實我們本質上是為了給每個線程維護一塊 HeapByteBuffer,用于緩存數據,并沒有必要以 ByteBuffer 的大小為維度來進行 IO??梢越梃b IOUtil 中復制 DirectByteBuffer 的思路來優化這一過程。代碼示例如下:

  1. public void directBufferCopy() throws Exception { 
  2.     File file = new File("/essd"); 
  3.     FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel(); 
  4.     ByteBuffer byteBuffer = ByteBuffer.allocate(50 * 1024 * 1024); 
  5.     ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(4 * 1024); 
  6.     for (int i = 0; i < 12800; i++) { 
  7.         directByteBuffer.clear(); 
  8.         fileChannel.read(directByteBuffer, i * 4 * 1024); 
  9.         directByteBuffer.flip(); 
  10.         byteBuffer.put(directByteBuffer); 
  11.     } 

在 Java 中,從磁盤到堆內內存,一定無法省略堆外內存的復制,但我們可以自己復制,從而使得這個過程更加直觀地被我們自己操控,而不是被 FileChannel 的內部邏輯左右。

這里也需要注意

單次 IO 使用的 DirectByteBuffer 不宜過大,僅僅作為一個運輸載體,起到一個運輸數據的作用。這樣在多線程場景下,才不至于占用過多的堆外內存

單次 IO 使用的 DirectByteBuffer 不宜過小,否則會出現讀寫放大的問題,一般建議設置 4kb 的整數倍,具體以實際測試結果為準。

其他注意事項

HeapByteBuffer 讀寫時的復制問題是本文的主角,但使用 HeapByteBuffer 作為緩存時,也需要注意一些其他問題。例如比賽場景中,你可能希望開辟一大塊 HeapByteBuffer,6G 堆內內存,分配個 4G 用作緩存總可以吧?可不可以我說了不算,你感興趣的話倒是可以測試一下是否可行,還需要考慮 GC 情況,需要綜合考慮老年代和新生代的配比,如果你分配了過多堆內內存給 HeapByteBuffer 緩存,可能會直接導致 OutOfMemory 或者觸發 GC。

同時,如果 HeapByteBuffer 占用了過多內存,留給操作系統的 PageCache 也會非常有限,這兩者使用的可是同一塊內存!如果你的程序利用到了 PageCache 的特性,可能會由于 PageCache 空間不夠,導致 IO 速度變慢。

總結

 

本文介紹了在文件 IO 中使用 HeapByteBuffer 的注意事項,需要考慮到 FileChannel 內部的復制問題,意識到這一過程會有堆外內存的復制開銷。在實際使用場景中,個人更加推薦直接使用 DirectByteBuffer 進行 IO 操作。如果出于某些原因,一定需要使用 HeapByteBuffer 存儲作為緩存,可以參考文中分批使用 DirectByteBuffer 進行 IO 并復制的方案。

 

責任編輯:武曉燕 來源: Kirito的技術分享
相關推薦

2010-11-26 16:27:01

MySQL使用變量

2011-07-25 17:48:10

iPhone 內存

2011-05-26 11:22:04

SEO

2012-03-12 16:46:22

NoSQL數據庫

2011-07-28 17:29:22

HBaseShell

2010-01-21 11:30:10

2010-01-18 14:25:19

使用C++Builde

2011-07-19 10:16:58

噴墨打印機注意事項

2011-07-21 15:40:24

iPhone 內存管理 對象

2010-07-20 13:02:03

SQL Server索

2013-09-25 10:15:51

閃存存儲優勢注意事項

2009-08-27 10:40:56

Java路徑

2011-07-26 18:43:34

HBase Shell

2011-08-02 13:08:06

Oracle索引

2011-04-14 11:28:07

光纖

2012-12-27 13:04:17

Android開發SQLite數據庫

2010-09-16 09:52:49

CSS display

2009-12-22 09:48:58

WCF枚舉類型

2009-12-15 17:47:17

VSIP

2009-06-12 09:46:40

Java String
點贊
收藏

51CTO技術棧公眾號

成人精品视频一区二区三区| 亚洲精品日韩综合观看成人91| 日本高清视频一区| 国产福利电影在线播放| 亚洲福利一区二区三区| 日本wwwwwwwzzzzz视频| 久久久久久久久久久久久久久99| 欧美另类网站| 亚洲精品123区| 999在线观看免费大全电视剧| 久久97久久97精品免视看秋霞| 深夜福利日韩在线看| 999av小视频在线| 欧美精品一区二| 欧美xxxbbb| 日韩成人在线播放| 牛牛精品一区二区| 日韩电影网在线| av片在线观看网站| 91精品婷婷国产综合久久性色 | 三上悠亚久久精品| 国产精品综合一区二区| 亚洲欧洲一区二区| 久草在线在线精品观看| 亚洲成人动漫在线| 精品一二三四区| 亚洲蜜桃在线| 国产乱色国产精品免费视频| 欧美做受777cos| 99国产精品国产精品毛片| 国内自拍在线观看| 久久精品亚洲精品国产欧美kt∨ | 中文天堂av| 亚洲国产欧美在线人成| 色久视频在线观看| 色婷婷综合五月| 午夜精品一区| 欧美videossexotv100| 4438x成人网全国最大| 亚洲国产精品专区久久| 成人精品国产亚洲| 91精品国产精品| 国产精品毛片一区二区在线看| 国产呦系列欧美呦日韩呦| 日本伊人色综合网| www.av中文字幕| 亚洲精品高清在线观看| 视频一区二区在线播放| 日韩一级片网址| 日韩色性视频| 国产一区视频在线播放| 久久精品午夜| 男女超爽视频免费播放| 亚洲日韩欧美一区二区在线| 欧美女优在线| 亚洲欧美日韩网| 欧美1区2区3区4区| 久久99精品国产一区二区三区| 国产成+人+日韩+欧美+亚洲| 久久久久久77777| 日韩欧美视频一区| 国产一区调教| 久久精品日产第一区二区三区乱码| 福利一区福利二区| 欧美伦理影视网| 久久精品国产v日韩v亚洲| 99国产**精品****| 蜜桃视频成人在线观看| 一区二区激情小说| 深夜福利视频一区二区| 日韩免费精品视频| 免费视频最近日韩| 成年人黄视频网站| 日韩av在线免播放器| 美女亚洲一区| 黄色一级片网址| 午夜成人免费视频| 91视频成人| 天天爽天天狠久久久| 亚洲一区二区三区三| 性欧美18~19sex高清播放| 国产精品96久久久久久| 国内精品免费**视频| 先锋影音欧美性受| 美女福利视频一区| 日韩av一区二区三区四区| 在线黄色国产电影| 欧美成人激情视频| 日韩电影在线观看一区| 91在线电影| 欧美日韩xxxxx| 国内精品写真在线观看| a天堂在线资源| 97免费在线视频| 国产电影精品久久禁18| 成年人在线观看视频| 91成人免费观看网站| 国产精品911| 欧美一区二区三区在线观看免费| 日本亚洲欧美三级| 久久久精品人体av艺术| 中文在线а√天堂| 日韩福利视频| 欧美精品18+| 欧美国内亚洲| 成人频在线观看| 91精品国产高清自在线| av电影在线观看完整版一区二区| 欧美xxxx视频| 欧美lavv| 欧美日韩国产一区| 欧美欧美全黄| 一区二区三区区四区播放视频在线观看| 久久这里只有精品视频首页| 国产伦精品一区二区三区免费 | 欧美日韩精品一区二区三区四区 | 日韩精品电影在线| 97视频在线观看网站| 成人a在线视频| 一区二区三区欧美久久| 国产成人免费视频网站视频社区| 视频一区视频二区视频| 欧美日韩激情在线| 亚洲天堂黄色| 触手亚洲一区二区三区| 亚洲精品日韩激情在线电影| 亚洲一区在线观看网站| 伊人精品一区| 91人人网站| 日韩**中文字幕毛片| 一区二区视频在线| 精品国产91乱码一区二区三区四区 | 欧美91在线| 97视频在线| 97人人做人人爱| 亚洲天堂av老司机| 林ゆな中文字幕一区二区| 99热一区二区| 国产97色在线| 疯狂欧美牲乱大交777| 欧美成人久久| caoporm免费视频在线| 欧美日本国产精品| 精品av久久707| 国产乱码精品一区二区三区五月婷| 三妻四妾的电影电视剧在线观看| 日本xxx免费| 最近2019年好看中文字幕视频| 91视频一区二区| 成人动漫视频| 五月天最新网址| av一区二区三区四区电影| 69堂成人精品免费视频| 日本伊人精品一区二区三区观看方式| 欧美在线极品| 国产男女激情视频| 国产精品久久999| 欧美日韩三级视频| 国产在线播精品第三| 成人短视频软件网站大全app| 激情丁香婷婷| 成人激情在线播放| 制服丝袜亚洲精品中文字幕| 狠狠色狠狠色综合日日91app| 国产精品一区二区精品视频观看| 超碰色偷偷男人的天堂| 国产精品一区视频网站| 亚洲精品一二区| 日本一区二区动态图| 久久精品亚洲人成影院| 91福利在线免费| 亚洲欧美日韩综合网| 99久热re在线精品996热视频 | 不卡在线视频| 少妇一晚三次一区二区三区| 久久久人成影片一区二区三区| 欧美日韩国产精品专区| 精品亚洲国产成人av制服丝袜| 日本在线视频一区二区三区| 中文资源在线网| 午夜探花在线观看| 国产精品成人v| 亚洲国产成人在线视频| 中文在线免费一区三区高中清不卡| 天天综合一区| 刘亦菲一区二区三区免费看| 国产字幕中文| 一区二区在线观| 国产精品久久久久福利| 精品处破学生在线二十三| 国产精品伦理在线| 天堂精品中文字幕在线| 亚洲毛片免费看| 伊人久久精品一区二区三区| 黄色污网站在线免费观看| 成人在线免费观看视频网站| 亚洲xxxx做受欧美| 久久免费少妇高潮久久精品99| 91精品国产综合久久久久| 国产欧美日韩不卡免费|