樂觀鎖與悲觀鎖總結
1007
2025-03-31
最近我學習了NIO相關的知識,然后發現了Netty這個基于NIO的網絡應用框架,于是就研究起Netty框架源碼,來好好體會一下網絡框架的設計理念和思想.
這個系列的文章不僅會總結Netty各個模塊的源碼原理,也會寫出一些自己對這些設計的理解和體會.
我基本按照并發編程網上這個系列文章的順序來進行系列文章的順序,不同的是我是基于Netty4.1的源碼進行分析和講解.
為了節約你的時間,本篇文章主要內容如下:
- Netty的Buffer的內存模型,涉及讀寫指針
- Netty的Buffer框架
- Netty的Pool原理,輕量對象池
Buffer
Java NIO中的Buffer用于和NIO通道進行交互,數據可以從通道讀入緩沖區,也可以從緩沖區寫入到通道中.所以說,Buffer其實就是一塊可以讀寫數據的內存,我們將其包裝為一個Java對象來提供一系列讀寫操作.
Netty并沒有直接使用Java NIO的Buffer實現,而是自己實現了一套Buffer框架來滿足自己的業務或者性能需求.
ByteBuf的基本原理
不同于NIO Buffer的讀寫指針共用原理,ByteBuf擁有readerIndex,writerIndex兩個指針.下面我們就來詳細的講解一下ByteBuf的內部原理.
+-------------------+------------------+------------------+ | discardable bytes | readable bytes | writable bytes | | | (CONTENT) | | +-------------------+------------------+------------------+ | | | | 0 <= readerIndex <= writerIndex <= capacity
1
2
3
4
5
6
從示意圖中我們可以看出readerIndex和writerIndex最多可以將整個內容空間劃分為三塊:廢棄區,可讀區和可寫區.下面我們就來看一下不同操作下的兩個指針的變化.
- 在初始化狀態下,假設capacity為20,readerIndex和writerIndex都為0,整個空間中只存在可寫區.此時只能寫,不能讀,進行讀操作會拋出異常.
+---------------------------------------------------------+ | writable bytes (got more space) | +---------------------------------------------------------+ | | readerIndex(0) writerIndex(0) <= capacity
1
2
3
4
5
6
寫入10個字節的數據,writerIndex指向10,readerIndex不會改變,所有內容空間中有可讀區和可寫區.大小都是10字節.
+-------------------+------------------+------------------+ | readable bytes | writable bytes | | (CONTENT) | | +--------- --------+------------------+------------------ | | | readerIndex(0) <= writerIndex(10) <= capacity
1
2
3
4
5
6
讀取5個字節的內容,writerIndex不變,readerIndex加5,指向了5.此時內容空間分為了5字節的廢棄區,5字節的可讀區和10字節的可寫區.
+-------------------+------------------+------------------+ | discardable bytes | readable bytes | writable bytes | | | (CONTENT) | | +-------------------+------------------+------------------+ | | | | 0 <= readerIndex(5) <= writerIndex(10) <= capacity
1
2
3
4
5
6
7
調用discardReadBytes方法后,將廢棄區的內容舍棄掉,readerIndex又指向了0,writerIndex指向了5,相當于可讀區和可寫區整體向左平移了5個字節.
+------------------+--------------------------------------+ | readable bytes | writable bytes (got more space) | +------------------+--------------------------------------+ | | | readerIndex (0) <= writerIndex (5) <= capacity
1
2
3
4
5
OS層次上Zero-copy,就是在操作數據時,不需要將數據buffer從一個內存區域拷貝到另一個內存區域,因為減少了一次內存的拷貝,因此CPU的效率得到了提升.
Netty的zero-copy體現在很多方面.比如Buffer的compose,duplicate,slice操作時不會拷貝底層的數據.而是通過ByteBuf對象的組合來實現上述的操作
- Netty提供了CompositeByteBuf類,可以將多個ByteBuf組合成一個邏輯上的Buffer,避免了各個buffer之間的拷貝,CompositeByteBuf并不擁有底層的數據,而是通過擁有兩個buffer對象,從這兩個buffer對象中獲取數據來對外提供看似合并了的數據.比如我們將一份協議數據的頭部buffer和消息體buffer合并成一個Buffer.
如上圖所示,所有底層的數據還是存儲在header和body這兩個真實的buffer中.
對于ByteBuf的slice和duplicate操作也是如此,不同的buffer共享了相同的底層數據,而不是進行底層數據的拷貝.具體使用到的Buffer類型為DuplicatedByteBuf和SlicedByteBuf.誰說是共享的底層數據,但是通過對writerIndex和readerIndex兩個指針的操作來實現slice和duplicate的功能.
Netty使用wrap操作將byte數組轉化為ByteBuf對象時,將byte數組包裹到對象中,而不是拷貝數組存放到對象中.
Netty 中使用 FileRegion 實現文件傳輸的零拷貝, 不過在底層 FileRegion 是依賴于 Java NIO FileChannel.transfer 的零拷貝功能.
Pool和Reference Count
4.0之后的版本實現了高性能的Buffer池,分配策略則是結合了buddy allocation和slab allocation的jemalloc變種,實現類為PoolArena,這樣的話,可以在頻繁分配和釋放Buffer時緩解GC壓力,還可以在初始化新buffer時減少內存帶寬消耗(初始化時不可避免的要給buffer數組賦初始值).
ByteBuf引入了Reference Count機制,你需要在不適用它的時候調用ReferenceCountUtil.release方法來減少它的引用.
后記
感覺自己在研究或在閱讀源代碼時還是有些問題,起始ByteBuf并不是Netty的關鍵所在,不應該花費這么長時間.以后還是要帶著目的來看源碼,不能把時間浪費在一些代碼細節上.
引用
https://segmentfault.com/a/1190000007560884
Java
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。