初識記憶體分配ByteBuf

2021-10-01 16:29:18 字數 3688 閱讀 5503

bytebuf 是netty 整個結構裡面最為底層的模組,主要負責把資料從底層io 裡面讀到bytebuf,然後傳遞給應用程式,應用程式處理完成之後再把資料封裝成bytebuf 寫回到io。所以,bytebuf 是直接與底層打交道的一層抽象。這塊內容,相對於netty 其他模組來說,是非常複雜的。從不同角度來分析bytebuf 的分配和**。主要從記憶體與記憶體管理器的抽象、不同規格大小和不同類別的記憶體的分配策略以及記憶體的**過程來展開。

我們可以首先來看一下原始碼中對bytebuf 的描述如下:

從上面bytebuf 的結構來看,我們發現bytebuf 有三個非常重要的指標,分別是readerindex(記錄讀指標的開始位置)、writerindex(記錄寫指標的開始位置)和capacity(緩衝區的總長度),三者的關係是readerindex<=writerindex<=capacity。然後,從0 到readerindex 為discardable bytes 表示是無效的,從readerindex 到writerindex 為readablebytes 表示可讀資料區,從writerindex 到capacity 為writable bytes 表示這段區間空閒可以往裡面寫資料。除了這三個指標,其實bytebuf 裡面還有乙個maxcapacity,這就相當於是bytebuf 擴容的最大閾值,我們看它的原始碼中有定義:

/*

*返回此緩衝區的最大允許容量。此值提供乙個上限

* returns the maximum allowed capacity of this buffer. this value provides an upper

* bound on .

*/public

abstract

int maxcapacity();

這個指標可以看做是capactiy 之後的這段,當netty 發現writable bytes 寫資料超出空間大小時,bytebuf 會提前幫我們自動擴容,擴容之後,就有了足夠的空間來寫資料,同時capactiy 也會同步更新,maxcapacity 就是擴容後capactiy的最大值。

接下來我們來看bytebuf 的基本api,主要包括read()、write()、set()以及mark()、reset()方法。我們用下面的**對bytebuf 最重要的api 做乙個詳細說明:

在netty 中,bytebuf 的大部分功能是在abstractbytebuf 中來實現的,我們可以先進入abstractbytebuf 的原始碼看看:

public

abstract

class

abstractbytebuf extends bytebuf

最重要的幾個屬性readerindex、writerindex、markedreaderindex、markedwriterindex、maxcapacity 被定義在abstractbytebuf 這個抽象類中,下面我們可以來看看基本讀寫的骨架**實現。例如,幾個基本的判斷讀寫區間的api,我們來看一下它的具體實現:

@override

public

boolean isreadable()

@override

public boolean isreadable(int

numbytes)

@override

public

boolean iswritable()

@override

public boolean iswritable(int

numbytes)

@override

public

intreadablebytes()

@override

public

intwritablebytes()

@override

public

intmaxwritablebytes()

@override

public

bytebuf markreaderindex()

@override

public bytebuf resetreaderindex()

@override

public bytebuf markwriterindex()

@override

public bytebuf resetwriterindex()

//再來看幾個讀寫操作的api,具體原始碼如下:

@override

public byte readbyte()

@override

public bytebuf writebyte(int value)

@override

public byte getbyte(int index)

protected abstract byte _getbyte(int index);
protected abstract void _setbyte(int index, int value);
我們看到,上面的**中readbyte()方法和getbyte()方法都呼叫了乙個抽象的_getbyte(),這個方法在abstractbytebuf的子類中實現。在writebyte()方法中有呼叫乙個抽象的_setbyte()方法,這個方法同樣也是在子類中實現。

abstractbytebuf 之下有眾多子類,大致可以從三個維度來進行分類,分別如下:

pooled:池化記憶體,就是從預先分配好的記憶體空間中提取一段連續記憶體封裝成乙個bytebuf 分給應用程式使用。

unsafe:是jdk 底層的乙個負責io 操作的物件,可以直接拿到物件的記憶體位址,基於記憶體位址進行讀寫操作。

direct:堆外記憶體,是直接呼叫jdk 的底層api 進行物理記憶體分配,不在jvm 的堆記憶體中,需要手動釋放。

綜上所述,其實bytebuf 一共會有六種組合:pooled 池化記憶體和unpooled 非池化記憶體;unsafe 和非unsafe;heap堆記憶體和direct 堆外記憶體。下圖是bytebuf 最重要的繼承關係類結構圖,通過命名就能一目了然:

bytebuf 最基本的讀寫api 操作在abstractbytebuf 中已經實現了,其眾多子類採用不同的策略來分配記憶體空間,下面對重要的幾個子類總結如下:

pooledheapbytebuf :池化的堆內緩衝區

pooledunsafeheapbytebuf :池化的unsafe 堆內緩衝區

pooleddirectbytebuf :池化的直接(堆外)緩衝區

pooledunsafedirectbytebuf :池化的unsafe 直接(堆外)緩衝區

unpooledheapbytebuf :非池化的堆內緩衝區

unpooledunsafeheapbytebuf :非池化的unsafe 堆內緩衝區

unpooleddirectbytebuf :非池化的直接(堆外)緩衝區

unpooledunsafedirectbytebuf :非池化的unsafe 直接(堆外)緩衝區

初步認識了bytebuf的結構,接下去我們來看看在netty中是怎麼管理這些buf及分配的。

記憶體分配 Go記憶體管理 記憶體分配一

go作為乙個比較新晚 新 的語言,自然借鑑前輩們的優點,比如說語言本身負責記憶體管理 對協程和高併發的高優支援 簡單高效的語法等。本篇及後續的幾篇要講的就是還沒提到的比較複雜的記憶體管理。學習記憶體管理 分配 前,如果有jvm的記憶體管理的基礎,會變得非常簡單,如果是第一次接觸記憶體管理,在看完go...

記憶體分配 定長記憶體分配器

在各種記憶體分配演算法中,有一種很實用,實現起來也簡單 定長的記憶體分配器。即每次分配的記憶體大小是固定的。大概邏輯是 在一些區域性的單執行緒邏輯中,可以有效提高效率。很短,很容易看懂 fallocator.h pragma once 固定長度的記憶體分配器 include include incl...

靜態記憶體分配和 動態記憶體分配

1 靜態記憶體分配是在編譯時完成的,不需要占用cpu資源 動態分配記憶體是在執行時完成的,動態記憶體的分配與釋放需要占用cpu資源 2 靜態記憶體分配是在棧上分配的,動態記憶體是堆上分配的 3 動態記憶體分配需要指標或引用資料型別的支援,而靜態記憶體分配不需要 4 靜態分配記憶體需要在編譯前確定記憶...