精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

感悟優化——Netty對JDK緩沖區的內存池零拷貝改造

存儲 存儲軟件
ByteBuf對ByteBuffer做了大量的優化,比如說內存池,零拷貝,引用計數(不依賴GC),本文主要是分析這些優化,學習這些優化思想,學以致用,在實際工程中,借鑒這些優化方案和思想。

NIO中緩沖區是數據傳輸的基礎,JDK通過ByteBuffer實現,Netty框架中并未采用JDK原生的ByteBuffer,而是構造了ByteBuf。

ByteBuf對ByteBuffer做了大量的優化,比如說內存池,零拷貝,引用計數(不依賴GC),本文主要是分析這些優化,學習這些優化思想,學以致用,在實際工程中,借鑒這些優化方案和思想。

[[240161]]

直接內存和堆內存

首先先講一下這里面需要用的基礎知識,在JVM中 內存可分為兩大塊,一個是堆內存,一個是直接內存。這里簡單介紹一下

堆內存:

堆內存是Jvm所管理的內存,相比方法區,棧內存,堆內存是***的一塊。所有的對象實例實例以及數組都要在堆上分配。

Java的垃圾收集器是可以在堆上回收垃圾。

直接內存:

JVM使用Native函數在堆外分配內存,之后通過Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。直接內存不會受到Java堆的限制,只受本機內存影響。

Java的GC只會在老年區滿了觸發Full GC時,才會去順便清理直接內存的廢棄對象。

JDK原生緩沖區ByteBuffer

在NIO中,所有數據都是用緩沖區處理的。讀寫數據,都是在緩沖區中進行的。緩存區實質是是一個數組,通常使用字節緩沖區——ByteBuffer。

屬性:

使用方式:

ByteBuffer可以申請兩種方式的內存,分別為堆內存和直接內存,首先看申請堆內存。

  1. // 申請堆內存  
  2. ByteBuffer HeapbyteBuffer = ByteBuffer.allocate(1024); 

很簡單,就一行代碼,再看看allocate方法。

  1. public static ByteBuffer allocate(int capacity) {        if (capacity < 0)            throw new IllegalArgumentException();        return new HeapByteBuffer(capacity, capacity);  
  2.     } 

其實就是new一個HeapByteBuffer對象。這個 HeapByteBuffer繼承自ByteBuffer,構造器采用了父類的構造器,如下所示:

  1. HeapByteBuffer(int cap, int lim) {            // package-private 
  2.  
  3.         super(-1, 0, lim, cap, new byte[cap], 0);        /* 
  4.         hb = new byte[cap]; 
  5.         offset = 0; 
  6.         */ 
  7.     }//ByteBuffer構造器 
  8.   ByteBuffer(int mark, int pos, int lim, int cap,   // package-private 
  9.                  byte[] hb, int offset) 
  10.     { 
  11.         super(mark, pos, lim, cap); 
  12.         this.hb = hb; 
  13.         this.offset = offset; 
  14.     } 

結合ByteBuffer的四個屬性,初始化的時候就可以賦值capaticy,limit,position,mark,至于byte[] hb, int offsef這兩個屬性,JDK文檔給出的解釋是 backing array , and array offset 。它是一個回滾數組,offset是數組的偏移值。

申請直接內存:

  1. // 申請直接內存  
  2.  ByteBuffer DirectbyteBuffer = ByteBuffer.allocateDirect(1024); 

allocateDirect()實際上就是new的一個DirectByteBuffer對象,不過這個new 一個普通對象不一樣。這里使用了Native函數來申請內存,在Java中就是調用unsafe對象

  1. public static ByteBuffer allocateDirect(int capacity) {        return new DirectByteBuffer(capacity); 
  2.     } 
  3.  
  4.  DirectByteBuffer(int cap) {                   // package-private 
  5.  
  6.         super(-1, 0, cap, cap); 
  7.         boolean pa = VM.isDirectMemoryPageAligned();        int ps = Bits.pageSize();        long size = Math.max(1L, (long)cap + (pa ? ps : 0)); 
  8.         Bits.reserveMemory(size, cap);        long base = 0;        try {            base = unsafe.allocateMemory(size); 
  9.         } catch (OutOfMemoryError x) { 
  10.             Bits.unreserveMemory(size, cap);            throw x; 
  11.         }        unsafe.setMemory(base, size, (byte) 0);        if (pa && (base % ps != 0)) {            // Round up to page boundary 
  12.             address = base + ps - (base & (ps - 1)); 
  13.         } else { 
  14.             address = base; 
  15.         } 
  16.         cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); 
  17.         att = null
  18.  
  19.     } 
  20. View Code

申請方法不同的內存有不同的用法。接下來看一看ByteBuffer的常用方法與如何使用

ByteBuffer的常用方法與使用方式

Bytebuf的讀和寫是使用put()和get()方法實現的

  1. // 讀操作public byte get() {    return hb[ix(nextGetIndex())]; 
  2. }final int nextGetIndex() {    if (position >= limit)        throw new BufferUnderflowException();    return position++; 
  3. }// 寫操作public ByteBuffer put(byte x) { 
  4.     hb[ix(nextPutIndex())] = x;    return this; 
  5. }final int nextPutIndex() {    if (position >= limit)        throw new BufferOverflowException();    return position++; 

從代碼中可以看出,讀和寫操作都會改變ByteBuffer的position屬性,這兩個操作是共用的position屬性。這樣就會帶來一個問題,讀寫操作會導致數據出錯啊,數據位置出錯。

ByteBuffer提供了flip()方法,讀寫模式切換,切換的時候會改變position和limit的位置。看看flip()怎么實現的:

  1. public final Buffer flip() {    // 1. 設置 limit 為當前位置 
  2.     limit = position;    // 2. 設置 position 為0 
  3.     position = 0; 
  4.     mark = -1;    return this; 

這里就不重點介紹了,有些細節可以自己去深究。

Netty的ByteBuf

Netty使用的自身的ByteBuf對象來進行數據傳輸,本質上使用了外觀模式對JDK的ByteBuffer進行封裝。

相較于原生的ByteBuffer,Netty的ByteBuf做了很多優化,零拷貝,內存池加速,讀寫索引。

為什么要使用內存池?

首先要明白一點,Netty的內存池是不依賴于JVM本身的GC的。

回顧一下直接內存的GC:

上文提到Java的GC只會在老年區滿了觸發Full GC時,才會去順便清理直接內存的廢棄對象。

JVM中的直接內存,存在堆內存中其實就是DirectByteBuffer類,它本身其實很小,真的內存是在堆外,這里是映射關系。

每次申請直接內存,都先看看是否超限 —— 直接內存的限額默認(可用 -XX:MaxDirectMemorySize 重新設定)。

如果超過限額,就會主動執行System.gc(),這樣會帶來一個影響,系統會中斷100ms。如果沒有成功回收直接內存,并且還是超過直接內存的限額,就會拋出OOM——內存溢出。

繼續從GC角度分析,DirectByteBuffer熬過了幾次young gc之后,會進入老年代。當老年代滿了之后,會觸發Full GC。

因為本身很小,很難占滿老年代,因此基本不會觸發Full GC,帶來的后果是大量堆外內存一直占著不放,無法進行內存回收。

還有***一個辦法,就是依靠申請額度超限時觸發的system.gc(),但是前面提到,它會中斷進程100ms,如果在這100ms的之間,系統未完成GC,仍會拋出OOM。

所以這個***一個辦法也不是完全保險的。

Netty使用了引用計數的方式,主動回收內存。回收的對象包括非池直接內存,和內存池中的內存。

內存池的內存泄露檢測?

Netty中使用引用計數機制來管理資源,ByteBuf實際上是實現了ReferenceCounted接口,當實例化ByteBuf對象時,引用計數加1。

當應用代碼保持一個對象引用時,會調用retain方法將計數增加1,對象使用完畢進行釋放,調用release將計數器減1.

當引用計數變為0時,對象將釋放所有的資源,返回內存池。

Netty內存泄漏檢測級別:

    禁用(DISABLED)   - 完全禁止泄露檢測。不推薦。

    簡單(SIMPLE)     - 告訴我們取樣的1%的緩沖是否發生了泄露。默認。

    高級(ADVANCED)   - 告訴我們取樣的1%的緩沖發生泄露的地方

    偏執(PARANOID)   - 跟高級選項類似,但此選項檢測所有緩沖,而不僅僅是取樣的那1%。此選項在自動測試階段很有用。如果構建(build)輸出包含了LEAK,可認為構建失敗也可以使用JVM的-Dio.netty.leakDetectionLevel選項來指定泄漏檢測級別。

內存跟蹤

在內存池中分配內存,得到的ByteBuf對象都是經過 toLeakAwareBuffer()方法封裝的,該方法作用就是對ByteBuf對象進行引用計數,使用 SimpleLeakAwareByteBuf或者 AdvancedLeakAwareByteBuf 來包裝ByteBuf。此外該方法只對非池內存中的直接內存和內存池中的內存進行內存泄露檢測。

  1. //裝飾器模式,用SimpleLeakAwareByteBuf或AdvancedLeakAwareByteBuf來包裝原始的ByteBufprotected static ByteBuf toLeakAwareBuffer(ByteBuf buf) { 
  2.         ResourceLeakTracker<ByteBuf> leak; 
  3.     //根據設置的Level來選擇使用何種裝飾器 
  4.         switch (ResourceLeakDetector.getLevel()) {            case SIMPLE:          //創建用于跟蹤和表示內容泄露的ResourcLeak對象 
  5.                 leak = AbstractByteBuf.leakDetector.track(buf);                if (leak != null) { 
  6.           //只在ByteBuf.order方法中調用ResourceLeak.record 
  7.                     buf = new SimpleLeakAwareByteBuf(buf, leak); 
  8.                 }                break;            case ADVANCED: 
  9.             case PARANOID: 
  10.                 leak = AbstractByteBuf.leakDetector.track(buf);                if (leak != null) { 
  11.           //只在ByteBuf.order方法中調用ResourceLeak.record 
  12.                     buf = new AdvancedLeakAwareByteBuf(buf, leak); 
  13.                 }                break;            default
  14.                 break; 
  15.         }        return buf; 
  16.     } 

實際上,內存泄露檢測是在 AbstractByteBuf.leakDetector.track(buf)進行的,來看看track方法的具體實現。

  1. /** 
  2.      * Creates a new {@link ResourceLeakTracker} which is expected to be closed via 
  3.      * {@link ResourceLeakTracker#close(Object)} when the related resource is deallocated. 
  4.      * 
  5.      * @return the {@link ResourceLeakTracker} or {@code null
  6.      */ 
  7.     @SuppressWarnings("unchecked"
  8.     public final ResourceLeakTracker<T> track(T obj) {        return track0(obj); 
  9.     }    @SuppressWarnings("unchecked"
  10.     private DefaultResourceLeak track0(T obj) { 
  11.         Level level = ResourceLeakDetector.level
  12.       // 不進行內存跟蹤 
  13.         if (level == Level.DISABLED) {            return null
  14.         }        if (level.ordinal() < Level.PARANOID.ordinal()) {         //如果監控級別低于PARANOID,在一定的采樣頻率下報告內存泄露 
  15.             if ((PlatformDependent.threadLocalRandom().nextInt(samplingInterval)) == 0) { 
  16.                 reportLeak();                return new DefaultResourceLeak(obj, refQueue, allLeaks); 
  17.             }            return null
  18.         }        //每次需要分配 ByteBuf 時,報告內存泄露情況 
  19.         reportLeak();        return new DefaultResourceLeak(obj, refQueue, allLeaks); 
  20.     } 

再來看看返回對象——DefaultResourceLeak,他的實現方式如下:

  1. private static final class DefaultResourceLeak<T>            extends WeakReference<Object> implements ResourceLeakTracker<T>, ResourceLeak { 

它繼承了虛引用WeakReference,虛引用完全不影響目標對象的垃圾回收,但是會在目標對象被VM垃圾回收時加入到引用隊列,

正常情況下ResourceLeak對象,會將監控的資源的引用計數為0時被清理掉。

但是當資源的引用計數失常,ResourceLeak對象也會被加入到引用隊列.

存在著這樣一種情況:沒有成對調用ByteBuf的retain和relaease方法,導致ByteBuf沒有被正常釋放,當 ResourceLeak(引用隊列) 中存在元素時,即表明有內存泄露。

Netty中的 reportLeak()方法來報告內存泄露情況,通過檢查引用隊列來判斷是否有內存泄露,并報告跟蹤情況.

方法代碼如下:

  1. View Code 

Handler中的內存處理機制

Netty中有handler鏈,消息有本Handler傳到下一個Handler。所以Netty引入了一個規則,誰是***使用者,誰負責釋放。

根據誰***使用誰負責釋放的原則,每個Handler對消息可能有三種處理方式

對原消息不做處理,調用 ctx.fireChannelRead(msg)把原消息往下傳,那不用做什么釋放。

將原消息轉化為新的消息并調用 ctx.fireChannelRead(newMsg)往下傳,那必須把原消息release掉。

如果已經不再調用ctx.fireChannelRead(msg)傳遞任何消息,那更要把原消息release掉。

假設每一個Handler都把消息往下傳,Handler并也不知道誰是啟動Netty時所設定的Handler鏈的***一員,所以Netty在Handler鏈的最末補了一個TailHandler,如果此時消息仍然是ReferenceCounted類型就會被release掉。

總結:

1.Netty在不同的內存泄漏檢測級別情況下,采樣概率是不一樣的,在Simple情況下出現了Leak,要設置“-Dio.netty.leakDetectionLevel=advanced”再跑一次代碼,找到創建和訪問的地方。

2.Netty中的內存泄露檢測是通過對ByteBuf對象進行裝飾,利用虛引用和引用計數來對非池中的直接內存和內存池中內存進行跟蹤,判斷是否發生內存泄露。

3.計數器基于 AtomicIntegerFieldUpdater,因為ByteBuf對象很多,如果都把int包一層AtomicInteger花銷較大,而AtomicIntegerFieldUpdater只需要一個全局的靜態變量。

Netty中的內存單位

Netty中將內存池分為五種不同的形態:Arena,ChunkList,Chunk,Page,SubPage.

首先來看Netty***的內存單位PoolArena——連續的內存塊。它是由多個PoolChunkList和兩個SubPagePools(一個是tinySubPagePool,一個是smallSubPagePool)組成的。如下圖所示:

 

1.PoolChunkList是一個雙向的鏈表,PoolChunkList負責管理多個PoolChunk的生命周期。

2.PoolChunk中包含多個Page,Page的大小默認是8192字節,也可以設置系統變量io.netty.allocator.pageSize來改變頁的大小。自定義頁大小有如下限制:1.必須大于4096字節,2.必須是2的整次數冪。

3.塊(PoolChunk)的大小是由頁的大小和maxOrder算出來的,計算公式是: chunkSize = 2^{maxOrder} * pageSize。 maxOrder的默認值是11,也可以通過io.netty.allocator.maxOrder系統變量設置,只能是0-14的范圍,所以chunksize的默認大小為:(2^11)*8192=16MB

Page中包含多個SubPage。

PoolChunk內部維護了一個平衡二叉樹,如下圖所示:

 

PoolSubPage

通常一個頁(page)的大小就達到了10^13(8192字節),通常一次申請分配內存沒有這么大,可能很小。

于是Netty將頁(page)劃分成更小的片段——SubPage

 

Netty定義這樣的內存單元是為了更好的分配內存,接下來看一下一個ByteBuf是如何在內存池中申請內存的。

Netty如何分配內存池中的內存?

分配原則:

內存池中的內存分配是在PoolArea中進行的。

  1. 申請小于PageSize(默認8192字節)的內存,會在SubPagePools中進行分配,如果申請內存小于512字節,則會在tingSubPagePools中進行分配,如果大于512小于PageSize字節,則會在smallSubPagePools進行分配。
  2. 申請大于PageSize的內存,則會在PoolChunkList中進行分配。
  3. 申請大于ChunkSize的內存,則不會在內存池中申請,而且也不會重用該內存。

應用中在內存池中申請內存的方法:

  1. // 在內存池中申請 直接內存 
  2.         ByteBuf directByteBuf = ByteBufAllocator.DEFAULT.directBuffer(1024);        // 在內存池中申請 堆內存 
  3.         ByteBuf heapByteBuf = ByteBufAllocator.DEFAULT.heapBuffer(1024); 

接下來,一層一層的看下來,在Netty中申請內存是如何實現的。就拿申請直接內存舉例,首先看directBuffer方法。

  1. // directBuffer方法實現 
  2.     @Override 
  3.     public ByteBuf directBuffer(int initialCapacity) {        return directBuffer(initialCapacity, DEFAULT_MAX_CAPACITY); 
  4.     }     
  5.     // 校驗申請大小,返回申請的直接內存 
  6.     @Override 
  7.     public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {        if (initialCapacity == 0 && maxCapacity == 0) {            return emptyBuf; 
  8.         } 
  9.         validate(initialCapacity, maxCapacity);        return newDirectBuffer(initialCapacity, maxCapacity); 
  10.     }    //PooledByteBufAllocator類中的 newDirectBuffer方法的實現 
  11.     @Override 
  12.     protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {        // Netty避免每個線程對內存池的競爭,在每個線程都提供了PoolThreadCache線程內的內存池 
  13.         PoolThreadCache cache = threadCache.get(); 
  14.         PoolArena<ByteBuffer> directArena = cache.directArena;       // 如果緩存存在,則分配內存 
  15.         final ByteBuf buf;        if (directArena != null) { 
  16.             buf = directArena.allocate(cache, initialCapacity, maxCapacity); 
  17.         } else {       // 緩存不存在,則分配非池內存 
  18.             buf = PlatformDependent.hasUnsafe() ? 
  19.                     UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :                    new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); 
  20.         } 
  21.     // 通過toLeakAwareBuffer包裝成內存泄漏檢測的buffer 
  22.         return toLeakAwareBuffer(buf); 
  23.     } 

一般情況下,內存都是在buf = directArena.allocate(cache, initialCapacity, maxCapacity)這行代碼進行內存分配的,也就是說在內存的連續塊PoolArena中進行的內存分配。

接下來,我們根據內存分配原則來進行內存研讀PoolArena中的allocate方法。

  1. 1     PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) { 2         PooledByteBuf<T> buf = newByteBuf(maxCapacity); 3         allocate(cache, buf, reqCapacity); 4         return buf; 5     } 6  
  2.  7     private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) { 8         final int normCapacity = normalizeCapacity(reqCapacity); 9         if (isTinyOrSmall(normCapacity)) { // capacity < pageSize10             int tableIdx;11             PoolSubpage<T>[] table;12             boolean tiny = isTiny(normCapacity);13             if (tiny) { // < 51214 15           // 如果申請內存小于512字節,則會在tingSubPagePools中進行分配16                 if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {17                     // was able to allocate out of the cache so move on18                     return;19                 }20                 tableIdx = tinyIdx(normCapacity);21                 table = tinySubpagePools;22             } else {23           // 如果大于512小于PageSize字節,則會在smallSubPagePools進行分配24                 if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {25                     // was able to allocate out of the cache so move on26                     return;27                 }28                 tableIdx = smallIdx(normCapacity);29                 table = smallSubpagePools;30             }31 32             final PoolSubpage<T> head = table[tableIdx];33 34             /** 
  3. 35              * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and 
  4. 36              * {@link PoolChunk#free(long)} may modify the doubly linked list as well. 
  5. 37              */38             synchronized (head) {39                 final PoolSubpage<T> s = head.next;40                 if (s != head) {41                     assert s.doNotDestroy && s.elemSize == normCapacity;42                     long handle = s.allocate();43                     assert handle >= 0;44                     s.chunk.initBufWithSubpage(buf, handle, reqCapacity);45                     incTinySmallAllocation(tiny);46                     return;47                 }48             }49             synchronized (this) {50                 allocateNormal(buf, reqCapacity, normCapacity);51             }52 53             incTinySmallAllocation(tiny);54             return;55         }56         if (normCapacity <= chunkSize) {57             if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {58                 // was able to allocate out of the cache so move on59                 return;60             }61             synchronized (this) {62                 allocateNormal(buf, reqCapacity, normCapacity);63                 ++allocationsNormal;64             }65         } else {66             // Huge allocations are never served via the cache so just call allocateHuge67             allocateHuge(buf, reqCapacity);68         }69     } 

如何使用內存池?

底層IO處理線程的緩沖區使用堆外直接緩沖區,減少一次IO復制。業務消息的編解碼使用堆緩沖區,分配效率更高,而且不涉及到內核緩沖區的復制問題。

Netty默認不使用內存池,需要在創建服務端或者客戶端的時候進行配置。

  1. //Boss線程池內存池配置. 
  2. .option(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT) //Work線程池內存池配置.  
  3. .childOption(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT); 

本人的想法是:

1.I/O處理線程使內存池中的直接內存,開啟以上配置

2.在handler處理業務的時候,使用內存池中的堆內存

還有一點值得注意的是:在使用完內存池中的ByteBuf,一定要記得釋放,即調用release():

  1. // 在內存池中申請 直接內存 
  2.         ByteBuf directByteBuf = ByteBufAllocator.DEFAULT.directBuffer(1024);        // 歸還到內存池 
  3.         directByteBuf.release(); 

如果handler繼承了SimpleChannelInboundHandler,那么它將會自動釋放Bytefuf.詳情可見:

  1. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        boolean release = true;        try {            if (acceptInboundMessage(msg)) {                @SuppressWarnings("unchecked"
  2.                 I imsg = (I) msg; 
  3.                 channelRead0(ctx, imsg); 
  4.             } else { 
  5.                 release = false
  6.                 ctx.fireChannelRead(msg); 
  7.             } 
  8.         } finally { 
  9.       // autoRelease默認為true 
  10.             if (autoRelease && release) { 
  11.       // 釋放Bytebuf,歸還到內存池 
  12.                 ReferenceCountUtil.release(msg); 
  13.             } 
  14.         } 
  15.     } 

零拷貝:

該部分是重點介紹的部分,首先將它與傳統的I/O read和write操作作對比,看看有什么不同,首先需要理解一下用戶態和內存態的概念

用戶態(User Mode)和內核態(Kernel Mode),也可以叫用戶空間和內核

用戶態:受限的訪問內存,并且不允許訪問硬件設備。

內核態:本質上是一個軟件,可以控制計算機的硬件資源(如網卡,硬盤),可以訪問內存所有數據。

用戶程序都是運行在用戶態中的,比如JVM,就是用戶程序,所以它運行在用戶態中。

用戶態是不能直接訪問硬件設備的,如果需要一次I/O操作,那就必須利用系統調用機制切換到內核態(用戶態與內核態之間的轉換稱為上下文切換),進行硬盤讀寫。

比如說一次傳統網絡I/O:

***步,從用戶態切換到內核態,將用戶緩沖區的數據拷貝到內核緩沖區,執行send操作。

第二步,數據發送由底層的操作系統進行,此時從內核態切換到用戶態,將內核緩存區的數據拷貝到網卡的緩沖區

總結:也就是一次普通的網絡I/O,至少經過兩次上下文切換,和兩次內存拷貝。

什么是零拷貝? 

當需要傳輸的數據遠大于內核緩沖區的大小時,內核緩沖區就成為I/O的性能瓶頸。零拷貝就是杜絕了內核緩沖區與用戶緩沖區的的數據拷貝。

所以零拷貝適合大數據量的傳輸。

拿傳統的網絡I/O做對比,零拷貝I/O是怎樣的一個過程:

用戶程序執行transferTo(),將用戶緩沖區待發送的數據拷貝到網卡緩沖區。

很簡單,一步完成,中間少了用戶態到內存態的拷貝。

Netty中零拷貝如何實現

Netty的中零拷貝與上述零拷貝是不一樣的,它并不是系統層面上的零拷貝,只是相對于ByteBuf而言的。

Netty中的零拷貝:

1.CompositeByteBuf,將多個ByteBuf合并為一個邏輯上的ByteBuf,避免了各個ByteBuf之間的拷貝。

使用方式:

  1. CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(); 
  2. compositeByteBuf.addComponents(true, ByteBuf1, ByteBuf1); 

注意: addComponents***個參數必須為true,那么writeIndex才不為0,才能從compositeByteBuf中讀到數據。

2.wrapedBuffer()方法,將byte[]數組包裝成ByteBuf對象。

  1. byte[] bytes = data.getBytes();ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); 

Unpooled.wrappedBuffer(bytes)就是進行了byte[]數組的包裝工作,過程中不存在內存拷貝。

即包裝出來的ByteBuf和byte[]數組指向了同一個存儲空間。因為值引用,所以bytes修改也會影響 byteBuf 的值。

3.ByteBuf的分割,slice()方法。將一個ByteBuf對象切分成多個ByteBuf對象。

  1. ByteBuf directByteBuf = ByteBufAllocator.DEFAULT.directBuffer(1024);ByteBuf header = directByteBuf.slice(0,50);ByteBuf body = directByteBuf.slice(51,1024); 

header和body兩個ByteBuf對象實際上還是指向directByteBuf的存儲空間。

總結:

本文很長很長,博主陸陸續續寫了有一個月的時間。但是只是窺探Netty內存池中的冰山一角,更多是要在實際項目中進行驗證才能起到效果。

 

責任編輯:武曉燕 來源: 漫談Java架構
相關推薦

2009-11-16 17:26:17

Oracle優化緩沖區

2011-07-20 10:54:14

C++

2021-12-09 09:30:38

字節流文件緩沖區

2009-11-16 16:59:24

Oracle優化庫高速

2025-04-27 08:25:00

Netty零拷貝內存

2022-05-13 09:02:34

LinuxBufferCache

2020-10-29 08:41:20

JavaNetty緩沖

2011-12-14 16:30:42

javanio

2019-02-27 13:58:29

漏洞緩沖區溢出系統安全

2017-01-09 17:03:34

2020-10-27 09:51:18

漏洞

2009-09-24 18:16:40

2017-07-04 17:09:10

Map環形緩沖區數據

2014-07-30 11:21:46

2018-01-26 14:52:43

2009-11-16 17:08:59

Oracle日志緩沖區

2010-12-27 10:21:21

2009-07-15 15:50:48

Jython線程

2023-10-09 23:01:09

MySQL數據庫

2010-10-09 14:45:48

點贊
收藏

51CTO技術棧公眾號

9191国产视频| 国产免费一区二区三区在线能观看 | 日韩高清三级| 国产免费黄色网址| 国产精品久久777777毛茸茸 | 一区二区视频在线播放| 成人免费视频国产| 美女视频一区在线观看| 性色av一区二区三区红粉影视| 一级黄色片大全| 日韩在线网址| 欧美色综合久久| 国内精品视频一区二区三区| 视频免费一区| 91丨porny丨国产| 亚洲一区二区三区777| 亚洲熟女综合色一区二区三区| 亚洲欧美网站在线观看| 亚洲色图25p| 久久久久久久人妻无码中文字幕爆| 2019年精品视频自拍| 亚洲va欧美va人人爽午夜| 亚洲欧洲一二三| 台湾av在线二三区观看| 国产1区2区3区精品美女| 国产精品流白浆视频| 久久露脸国语精品国产91| 91精品国产福利在线观看麻豆| 亚洲精品在线91| 国产精品日日摸夜夜爽| 不卡一区视频| 欧美乱妇15p| 污片在线免费看| 345成人影院| 偷拍亚洲欧洲综合| 妞干网视频在线观看| 国产成人永久免费视频| av无码av天天av天天爽| 99久久婷婷国产综合精品青牛牛 | 免费激情视频在线观看| 九色porny丨国产首页在线| 亚洲免费高清视频在线| 二级片在线观看| 精品176二区| 国产精品久久久久久久久免费樱桃 | 国产一区二区精品久| 亚洲精品xxxx| 男人添女人荫蒂国产| 精品入口麻豆88视频| 91麻豆精品国产91久久久使用方法 | 91女神在线视频| 久久精品日产第一区二区三区精品版| 欧洲av在线播放| 成人久久18免费网站麻豆| 粉嫩av免费一区二区三区| 好吊视频一区二区三区| 成人黄页在线观看| 国产专区一区二区三区| 亚洲av成人精品一区二区三区在线播放 | 91精品在线观看国产| 久久午夜a级毛片| 日本在线一级片| 欧美日韩福利| 2018日韩中文字幕| 精品成人无码久久久久久| 日本免费新一区视频| 国产精品偷伦免费视频观看的| 亚洲综合免费视频| 国产精品99久久久久久似苏梦涵| 99视频免费观看蜜桃视频| 空姐吹箫视频大全| 26uuu亚洲综合色欧美| 日韩精品久久一区二区三区| 1769在线观看| 夜夜精品浪潮av一区二区三区| 黄色网络在线观看| 久久香蕉av| 色综合一个色综合亚洲| 五月婷婷六月丁香激情| 亚洲国产视频二区| 日韩av在线免费播放| 国产一区二区三区四区五区六区| 国产精品成人一区二区不卡| 欧美激情在线视频二区| 波多野结衣视频网址| 国产麻豆视频精品| 久久本道综合色狠狠五月| www.久久热.com| 亚洲尤物视频在线| 欧美成人黑人猛交| 激情视频亚洲| 亚洲男人av在线| 草视频在线观看| 久色成人在线| www国产亚洲精品| 国产日本在线观看| 夜夜嗨av一区二区三区四季av| 免费观看日韩毛片| 韩国三级成人在线| 国产午夜精品一区理论片飘花| 黄色一级视频免费观看| 视频一区免费在线观看| 成人综合色站| 日韩在线资源| 一本大道色婷婷在线| 欧美色图12p| 亚洲天堂成人av| 911精品美国片911久久久| 欧美在线日韩在线| 精品国自产在线观看| 日本一区二区动态图| 日本a在线免费观看| 国产色99精品9i| 在线精品国产欧美| 国产a∨精品一区二区三区仙踪林| 激情综合亚洲精品| 欧美一区二区高清在线观看| 草莓视频丝瓜在线观看丝瓜18| 欧美精品乱人伦久久久久久| av中文字幕免费观看| 亚洲精品日本| 国产精品对白刺激久久久| 麻豆影院在线| 欧美日韩国产精选| 国产美女精品久久| 国产亚洲在线| 国产综合 伊人色| 黄色成人在线网| 日韩你懂的在线观看| 一级黄色片日本| 久久国产婷婷国产香蕉| 亚洲二区自拍| 欧美日韩国产网站| 亚洲午夜未满十八勿入免费观看全集| 日产欧产va高清| 成人app下载| 97视频在线免费| **爰片久久毛片| 欧美俄罗斯乱妇| 亚洲精品国产精| 一卡二卡三卡日韩欧美| 老熟女高潮一区二区三区| 国产精品国码视频| 国产精品久久精品视| 欧美家庭影院| 亚洲精品一区二区三区99| 国产一级免费av| www.欧美色图| 久久国产亚洲精品无码| 综合亚洲色图| 国产福利视频一区二区| 啊v视频在线| 欧美日韩成人综合| 黄色a级片在线观看| 国产美女娇喘av呻吟久久| 亚洲欧美日韩精品综合在线观看| 四虎国产精品成人免费影视| 美女扒开尿口让男人操亚洲视频网站| 99热这里只有精品99| 亚洲一区在线观看免费| www.17c.com喷水少妇| 亚洲一区国产一区| 视频二区一区| 免费精品一区| 97久久久免费福利网址| 精品无吗乱吗av国产爱色| 欧美性欧美巨大黑白大战| 顶级黑人搡bbw搡bbbb搡| 国产精品一二二区| 乱人伦xxxx国语对白| 精品久久久久久久久久久下田| 国产在线精品自拍| 丁香花在线电影| 亚洲欧洲日产国码av系列天堂| 亚洲视屏在线观看| 亚洲激情男女视频| 亚洲国产精品自拍视频| 日本免费在线视频不卡一不卡二| 成人午夜免费剧场| 欧美韩一区二区| 国产日韩在线免费| xxx.xxx欧美| 在线观看欧美日韩| 亚洲国产精品国自产拍久久| 一本一本大道香蕉久在线精品| 精品国产国产综合精品| 成人国产电影网| 亚洲欧美日韩一级| 亚洲精品偷拍| 一区二区三区欧美成人| 精品欠久久久中文字幕加勒比| 国产精欧美一区二区三区| 日韩少妇视频| 日韩在线视频国产| 三级小视频在线观看| 欧美日韩国产乱码电影| 毛片在线免费视频| 亚洲欧洲综合另类| 一级片视频免费看| 懂色av一区二区三区免费观看| 91蝌蚪视频在线观看| 黄色av成人| 中文字幕中文字幕在线中心一区 | 久久电影网站中文字幕| 毛片在线视频播放| 欧美在线精品一区| 日韩欧美在线观看强乱免费| 国产成人在线中文字幕| 91理论片午午论夜理片久久| 日韩影片中文字幕| 久久久久久久久久久91| 成人在线app| 中文字幕久精品免费视频| 污视频软件在线观看| 日韩一区二区三区高清免费看看| 国产日韩久久久| 天天操天天干天天综合网| 日韩高清dvd碟片| 国产精品视频一二| 久操视频免费看| 成人深夜福利app| 国产黑丝在线视频| 九一九一国产精品| 91香蕉视频污版| 久久久999| 97av视频在线观看| 99国产精品| 国内精品视频一区二区三区| 国产综合亚洲精品一区二| 老汉色影院首页| 亚洲天天影视网| 国产又粗又爽又黄的视频| 99热在线成人| 亚洲免费精品视频| 青青一区二区三区| 亚洲国产一区二区精品视频| 国产一区二区三区网| 日韩精品伦理第一区| 国产日产精品一区二区三区四区的观看方式 | aaaaa黄色片| 国产一区二区网址| 天天色天天干天天色| 国产一区二区调教| 欧美性猛交乱大交| 国产激情一区二区三区| 可以看的av网址| 风间由美一区二区三区在线观看| 超级砰砰砰97免费观看最新一期| 国产老女人精品毛片久久| 蜜桃视频无码区在线观看| 国产成人三级在线观看| 扒开伸进免费视频| 91在线视频官网| 成人国产精品久久久网站| 国产喷白浆一区二区三区| 秋霞网一区二区三区| 亚洲欧洲日韩综合一区二区| 91日韩中文字幕| 亚洲图片欧美色图| 久久久午夜影院| 在线观看免费成人| 91成人一区二区三区| 日韩亚洲欧美综合| 色婷婷av一区二区三| 国产一区二区三区在线观看网站| av播放在线| 九九热精品视频| 午夜久久中文| 91免费高清视频| 老司机精品在线| 偷拍视频一区二区| 女同性一区二区三区人了人一| 久久久久99精品成人片| 性一交一乱一区二区洋洋av| 日本肉体xxxx裸体xxx免费| 国产激情一区二区三区| v8888av| 国产精品理论片在线观看| 久久久久久久久毛片| 日韩欧美国产激情| 一本到在线视频| 亚洲黄色在线看| 在线视频自拍| 久久久综合av| 免费视频观看成人| 国产亚洲精品美女久久久m| 狠狠做深爱婷婷综合一区| 免费看欧美一级片| 免费在线看成人av| www.555国产精品免费| 亚洲国产激情av| 国产一级淫片a| 欧美日韩激情一区二区三区| 日韩一区二区三区不卡| 日韩视频亚洲视频| 亚洲精品福利电影| 97久草视频| blacked蜜桃精品一区| aa视频在线播放| 国内精品国产成人| 91中文字幕永久在线| 亚洲影院理伦片| 97在线播放免费观看| 亚洲女人被黑人巨大进入| 性直播体位视频在线观看| 国产精品国产福利国产秒拍| 都市激情久久| 日韩精品第1页| 美女视频黄久久| 精品无码在线视频| 亚洲一区中文在线| 亚洲无码精品在线观看| 亚洲欧美国产精品专区久久 | 色菇凉天天综合网| 欧美一级淫片aaaaaa| 久久五月情影视| 久久麻豆视频| 日韩在线电影一区| 久久婷婷激情| 中文字幕丰满孑伦无码专区| 亚洲福利视频一区二区| 99re只有精品| 久久久精品美女| 成人在线视频区| 永久久久久久| 麻豆精品一区二区av白丝在线| 短视频在线观看| 午夜不卡av免费| 少妇人妻一区二区| 97精品国产97久久久久久免费| 一区二区三区免费在线看| 美女在线免费视频| 狠狠狠色丁香婷婷综合激情| 亚洲区一区二区三| 欧美高清精品3d| 午夜激情在线观看| 国产精品亚洲自拍| 天天色天天射综合网| 中文字幕久久av| 18欧美乱大交hd1984| 国产在成人精品线拍偷自揄拍| www.日韩av.com| 北岛玲精品视频在线观看| 免费国产成人看片在线| 国产一区在线看| 青青草手机在线视频| 精品奇米国产一区二区三区| 日本理论片午伦夜理片在线观看| 成人免费视频视频在| 雨宫琴音一区二区在线| 亚洲精品女人久久久| 日韩欧美在线视频观看| 国产美女性感在线观看懂色av | 国产免费观看高清视频| 99久久精品国产麻豆演员表| 波多野结衣视频网站| 亚洲欧美中文另类| 成人免费在线观看视频| 妞干网这里只有精品| 粉嫩久久99精品久久久久久夜| 在线看成人av| 国产香蕉97碰碰久久人人| 国产亚洲精彩久久| 国产盗摄视频在线观看| 成人sese在线| 丁香社区五月天| 久久精品人人做人人爽| 成人在线超碰| 日日碰狠狠躁久久躁婷婷| 国产精品美女一区二区| a级片免费视频| 欧美诱惑福利视频| 欧美高清视频手机在在线| 人妻精品久久久久中文字幕69| 香蕉久久一区二区不卡无毒影院 | 精品人妻aV中文字幕乱码色欲| 午夜精品久久久久久久白皮肤| 久久成人av| 精品人妻一区二区乱码| 欧美日韩精品在线视频| 日韩免费网站| 久久久久免费网| 精品一区二区国语对白| 午夜影院在线看| 久久九九有精品国产23| 美女久久99| 无码人妻丰满熟妇区毛片蜜桃精品 | 日韩高清一级| 天堂av手机在线| 色综合久久99| 日本精品600av| 亚洲国产精品视频一区| 白白色 亚洲乱淫| 国产内射老熟女aaaa∵| 日韩av免费在线观看| 午夜精品亚洲| 国产精品国产三级国产专业不 | 欧美综合久久久| av资源一区| 只有这里有精品|