列存儲(CStoreMemAlloc)
- 概述
- CStoreMemAlloc 類
- CStoreMemAlloc::Palloc 函數
- CStoreMemAlloc::AllocPointerNode 函數
- CStoreMemAlloc::FreePointerNode 函數
- CStoreMemAlloc::Repalloc 函數
- CStoreMemAlloc::Pfree
- CStoreMemAlloc::Register 函數
- CStoreMemAlloc::Unregister 函數
- CStoreMemAlloc::Reset 函數
- CStoreMemAlloc::Init 函數
- 總結
聲明:本文的部分內容參考了他人的文章。在編寫過程中,我們尊重他人的知識產權和學術成果,力求遵循合理使用原則,并在適用的情況下注明引用來源。
本文主要參考了 OpenGauss1.1.0 的開源代碼和《OpenGauss數據庫源碼解析》一書以及OpenGauss社區學習文檔和一些參考資料
概述
??學習完了 CU 和 CUStorage 類,我們最后來學習一下 CStoreMemAlloc 類。CStoreMemAlloc 類提供了內存管理的功能,而 CU 和 CUStorage 類負責處理列存儲中的具體數據單元和物理存儲。在實際的列存儲系統中,這些類可能會協同工作以有效地管理內存和存儲數據。
CStoreMemAlloc 類
??CStoreMemAlloc 類在列存儲中扮演了一個內存管理的角色,幫助管理和釋放與列存儲操作相關的動態內存。這對于確保系統在處理大量數據時能夠高效使用內存、防止內存泄漏以及正確處理事務回滾等方面非常重要。類的定義如下:(函數路徑:src/include/storage/cstore/cstore_mem_alloc.h
)
// 類定義:CStoreMemAlloc
// 用于管理從malloc中分配的內存指針
// 在事務出現錯誤時,可以在abortTransaction中重置malloc的內存
// 在事務提交時,可以重置malloc的內存
class CStoreMemAlloc {
public:static void* Palloc(Size size, bool toRegister = true);static void Pfree(void* pointer, bool registered = true);static void* Repalloc(void* pointer, Size size, Size old_size, bool registered = true);static void Register(void* pointer);static void Unregister(const void* pointer);static void Reset();static void Init();private:static void* AllocPointerNode();static void FreePointerNode(PointerNode* pointer);// 保存所有分配的內存指針數組static THR_LOCAL PointerList m_tab[MaxPointersArryLen];// 記錄分配的內存指針數量static THR_LOCAL uint64 m_count;// 保存已經分配的PointerNode的緩存static THR_LOCAL uint32 m_ptrNodeCacheCount;static THR_LOCAL PointerNode* m_ptrNodeCache[MaxPtrNodeCacheLen];
};
??下面我們分別來看看CStoreMemAlloc 類成員函數的作用是什么?
CStoreMemAlloc::Palloc 函數
??CStoreMemAlloc 類中的 Palloc 方法。該方法用于從系統中分配內存,并根據需要將內存指針注冊到管理中。具體作用和功能如下:
描述:該方法用于從系統中分配內存,支持動態內存的分配操作。
參數:size:需要分配的內存大小。toRegister:表示是否需要將該內存指針在事務管理范圍內進行注冊。如果為 true,則注冊;如果為 false,則不注冊。
功能:確保要分配的內存大小大于0。調用內部的 InnerMalloc 方法進行實際的內存分配。如果內存分配失敗,拋出內存不足的錯誤。如果需要在事務管理范圍內注冊該內存指針,則進行注冊。返回分配的內存指針。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 從系統中分配內存* @Param[IN] size: 需要的內存大小* @Param[IN] toRegister: 如果這塊內存在線程管理的范圍內,則設置為 true;* 如果這塊內存在進程管理的范圍內,則設置為 false。* @See also: 無*/
void* CStoreMemAlloc::Palloc(Size size, bool toRegister)
{// 確保分配的內存大小大于0Assert(size > 0);// 調用內部的分配方法分配內存void* ptr = InnerMalloc(size);// 如果分配失敗,拋出內存不足的錯誤if (ptr == NULL) {ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("malloc fails, out of memory: size %lu", size)));}// 如果需要注冊(在事務管理范圍內),則進行注冊if (toRegister) {/* 步驟2:注冊指針 */Register(ptr);}// 返回分配的內存指針return ptr;
}
CStoreMemAlloc::AllocPointerNode 函數
??這段代碼是 CStoreMemAlloc 類中的 AllocPointerNode 方法。該方法用于分配一個指針節點,具體作用和功能如下:
描述:該方法用于分配一個指針節點,該節點可能被用于管理 CStoreMemAlloc 類中的內存指針。
返回:返回分配得到的指針節點。
功能:如果指針節點緩存中有可用節點,從緩存中取出一個節點。如果指針節點緩存中沒有可用節點,直接使用 malloc 分配一個新的節點。如果分配失敗,拋出內存不足的錯誤。返回分配得到的指針節點。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 分配一個指針節點* @Return: 分配得到的指針節點* @See also: 無*/
void* CStoreMemAlloc::AllocPointerNode()
{PointerNode* ptr = NULL;// 如果緩存中有可用節點,從緩存中取出if (m_ptrNodeCacheCount > 0) {ptr = m_ptrNodeCache[--m_ptrNodeCacheCount];} else {// 如果緩存中沒有可用節點,直接使用malloc分配新節點ptr = (PointerNode*)malloc(sizeof(PointerNode));// 分配失敗則拋出內存不足的錯誤if (ptr == NULL) {ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("malloc fails, out of memory")));}}// 返回分配得到的指針節點return ptr;
}
CStoreMemAlloc::FreePointerNode 函數
??CStoreMemAlloc 類中的 FreePointerNode 方法用于釋放一個指針節點,具體作用和功能如下:
描述:該方法用于釋放一個指針節點,該節點可能被用于管理 CStoreMemAlloc 類中的內存指針。
參數:ptr 為要釋放的指針節點。
功能:如果指針節點緩存未滿,將節點放入緩存中以便后續復用。如果指針節點緩存已滿,直接使用 free 釋放節點。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 釋放一個指針節點* @Param[IN] ptr: 要釋放的指針節點* @See also: 無*/
void CStoreMemAlloc::FreePointerNode(PointerNode* ptr)
{// 如果指針節點緩存未滿,將節點放入緩存中if (m_ptrNodeCacheCount < MaxPtrNodeCacheLen) {m_ptrNodeCache[m_ptrNodeCacheCount++] = ptr;} else {// 如果緩存已滿,直接使用free釋放節點free(ptr);}
}
CStoreMemAlloc::Repalloc 函數
??CStoreMemAlloc 類中的 Repalloc 方法用于重新分配內存(相當于 remalloc 操作),并根據需要注冊新分配的內存,取消注冊舊內存。具體作用和功能如下:
描述:該方法用于重新分配內存,類似于系統中的 remalloc 操作,同時支持內存的注冊和取消注冊。
參數:old_size:舊內存的大小。size:新內存的大小。pointer:要釋放的舊內存指針。registered:如果在調用 Palloc() 時傳遞 true,則為 true;如果傳遞 false,則為 false。
功能:斷言,確保內存指針和大小的合法性。調用內部的 InnerMalloc 方法重新分配內存。如果分配失敗,拋出內存不足的錯誤。將舊內存中的數據拷貝到新分配的內存中。如果在 Palloc() 中傳遞 true,則注冊新分配的內存,取消注冊舊內存。斷言,確保計數器大于0。釋放舊內存。返回新分配的內存指針。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 重新分配內存(remalloc)從系統中* @Param[IN] old_size: 舊內存大小* @Param[IN] size: 新內存大小* @Param[IN] pointer: 要釋放的內存指針* @Param[IN] registered: 如果在 Palloc() 中傳遞 true,則為 true;* 如果在 Palloc() 中傳遞 false,則為 false。* @See also: 無*/
void* CStoreMemAlloc::Repalloc(void* pointer, Size size, Size old_size, bool registered)
{// 斷言,確保內存指針和大小的合法性Assert(pointer != NULL);Assert(size > 0);// 調用內部的 InnerMalloc 方法重新分配內存void* ptr = InnerMalloc(size);// 如果分配失敗,拋出內存不足的錯誤if (ptr == NULL) {ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));}// 將舊內存中的數據拷貝到新分配的內存中errno_t rc = memcpy_s(ptr, size, pointer, old_size);securec_check_c(rc, "\0", "\0");// 如果在 Palloc() 中傳遞 true,則注冊新分配的內存,取消注冊舊內存if (registered) {Register(ptr);Unregister(pointer);// 斷言,確保計數器大于0Assert(m_count > 0);}// 釋放舊內存free(pointer);// 返回新分配的內存指針return ptr;
}
CStoreMemAlloc::Pfree
??CStoreMemAlloc 類中的 Pfree 方法用于將內存釋放到系統,同時根據需要取消注冊內存指針。具體作用和功能如下:
描述:該方法用于釋放內存到系統,可以選擇是否取消注冊內存指針。
參數:pointer:要釋放的內存指針。registered:如果在調用 Palloc() 時傳遞 true,則為 true;如果傳遞 false,則為 false。
功能:斷言,確保內存指針的合法性。如果在 Palloc() 中傳遞 true,則取消注冊內存指針。釋放內存到系統。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 釋放內存到系統* @Param[IN] pointer: 要釋放的內存指針* @Param[IN] registered: 如果在 Palloc() 中傳遞 true,則為 true;* 如果在 Palloc() 中傳遞 false,則為 false。* @See also: 無*/
void CStoreMemAlloc::Pfree(void* pointer, bool registered)
{// 斷言,確保內存指針的合法性Assert(pointer);// 如果在 Palloc() 中傳遞 true,則取消注冊內存指針if (registered) {Unregister(pointer);}// 釋放內存到系統free(pointer);
}
CStoreMemAlloc::Register 函數
??CStoreMemAlloc 類中的 Register 方法用于注冊內存指針,將其添加到內存指針管理表中。具體作用和功能如下:
描述:該方法用于將內存指針注冊,將其添加到內存指針管理表中,以便在事務結束時進行統一的處理。
參數:pointer:要注冊的內存指針。
功能:斷言,確保 MaxPointersArryLen 是2的冪。計算內存指針在管理表中的索引位置。如果鏈表為空,頭尾節點都指向新分配的節點。如果鏈表不為空,在鏈表尾部添加新節點。增加內存指針計數器。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 注冊內存指針* @Param[IN] pointer: 要注冊的內存指針* @See also: AllocPointerNode()*/
void CStoreMemAlloc::Register(void* pointer)
{// 斷言,確保 MaxPointersArryLen 是2的冪Assert((MaxPointersArryLen & (MaxPointersArryLen - 1)) == 0);// 計算索引位置int idx = PointerGetDatum(pointer) & (MaxPointersArryLen - 1);PointerNode* nodePtr = m_tab[idx].tail;// 如果尾節點為空,說明鏈表為空if (nodePtr == NULL) {// 頭尾節點都指向新分配的節點Assert(m_tab[idx].header == NULL);m_tab[idx].header = m_tab[idx].tail = (PointerNode*)AllocPointerNode();m_tab[idx].header->ptr = pointer;m_tab[idx].header->next = NULL;} else {// 否則在鏈表尾部添加新節點nodePtr->next = (PointerNode*)AllocPointerNode();nodePtr->next->ptr = pointer;nodePtr->next->next = NULL;m_tab[idx].tail = nodePtr->next;}// 增加計數器++m_count;
}
CStoreMemAlloc::Unregister 函數
??CStoreMemAlloc 類中的 Unregister 方法用于取消注冊內存指針,將其從內存指針管理表中移除。具體作用和功能如下:
描述:該方法用于取消注冊內存指針,將其從內存指針管理表中移除,以便在事務結束時進行統一的處理。
參數:pointer:要取消注冊的內存指針。
功能:斷言,確保內存指針和計數器的合法性。確定包含該指針的節點列表。在節點列表中查找指針,如果找到則移除該節點。如果取消注冊的是尾節點,需要修改尾指針。重置并釋放節點。減少計數器。斷言,確保找到了要取消注冊的指針節點。
??函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 取消注冊內存指針* @Param[IN] pointer: 要取消注冊的內存指針* @See also: FreePointerNode()*/
void CStoreMemAlloc::Unregister(const void* pointer)
{// 斷言,確保內存指針和計數器的合法性Assert(pointer && m_count > 0);// Step 1: 確定包含該指針的節點列表Assert((MaxPointersArryLen & (MaxPointersArryLen - 1)) == 0);int idx = PointerGetDatum(pointer) & (MaxPointersArryLen - 1);// Step 2: 在節點列表中查找指針PointerNode* nodePtr = m_tab[idx].header;Assert(nodePtr);PointerNode* prePtr = NULL;while (nodePtr != NULL) {if (nodePtr->ptr == pointer) {// 如果前一個節點為空,說明要取消注冊的是頭節點if (prePtr == NULL) {m_tab[idx].header = nodePtr->next;// 如果這個列表只有一個節點if (m_tab[idx].tail == nodePtr) {Assert(m_tab[idx].header == NULL);m_tab[idx].tail = NULL;}} else {// 否則,修改前一個節點的 next 指針prePtr->next = nodePtr->next;// 如果取消注冊的是尾節點,需要修改尾指針if (m_tab[idx].tail == nodePtr) {m_tab[idx].tail = prePtr;Assert(m_tab[idx].tail->next == NULL);}}// 重置并釋放節點nodePtr->Reset();FreePointerNode(nodePtr);break;}prePtr = nodePtr;nodePtr = nodePtr->next;}// 減少計數器--m_count;// 斷言,確保找到了要取消注冊的指針節點Assert(nodePtr != NULL);
}
CStoreMemAlloc::Reset 函數
??CStoreMemAlloc 類中的 Reset 方法用于重置內存指針管理器的狀態,釋放所有注冊的內存。具體作用和功能如下:
描述:該方法用于在事務結束時,重置內存指針管理器的狀態,釋放所有注冊的內存。
功能:如果存在注冊的內存指針:Step 1: 釋放 m_tab 中的每個列表中的內存指針。遍歷 m_tab 數組,釋放每個列表中的內存指針。注意,必須將列表的頭尾節點重置為 NULL,因為線程可能被重用。斷言,確保釋放的內存數和計數器一致。Step 2: 釋放緩存的節點(如果有的話)。釋放緩存的節點,以便在下一次使用時重新分配。
??這個方法的目的是確保在事務結束時,所有注冊的內存都被正確釋放,以避免內存泄漏。函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 重置內存指針管理器狀態,釋放所有注冊的內存*/
void CStoreMemAlloc::Reset()
{uint32 i = 0;uint32 freeNum = 0;// 如果存在注冊的內存指針if (m_count) {// Step 1: 釋放 m_tab 中的每個列表for (i = 0; i < MaxPointersArryLen; ++i) {PointerNode* nodePtr = m_tab[i].header;PointerNode* tmpPtr = NULL;// 釋放每個列表中的內存指針while ((nodePtr != NULL) && (nodePtr->ptr != NULL)) {free(nodePtr->ptr);tmpPtr = nodePtr->next;free(nodePtr);nodePtr = tmpPtr;++freeNum;}// 注意,必須將 header 和 tail 重置為 NULL// 因為線程可能被重用m_tab[i].header = NULL;m_tab[i].tail = NULL;}// 斷言,確保釋放的內存數和計數器一致Assert(m_count == freeNum);m_count = 0;}// Step 2: 釋放緩存的節點(如果有的話)for (i = 0; i < m_ptrNodeCacheCount; ++i) {free(m_ptrNodeCache[i]);}m_ptrNodeCacheCount = 0;
}
CStoreMemAlloc::Init 函數
??這段代碼是 CStoreMemAlloc 類中的 Init 方法。該方法用于初始化內存指針管理器的狀態。具體作用和功能如下:
描述:該方法用于在開始新的事務時,初始化內存指針管理器的狀態。
功能:重置計數器。斷言,確保 MaxPointersArryLen 是2的冪。初始化 m_tab 數組,將每個列表的頭尾節點都設置為 NULL。初始化緩存節點計數器。
??這個方法的目的是確保在每次開始新的事務時,內存指針管理器都處于初始狀態。函數源碼如下所示:(路徑:src/gausskernel/storage/cstore/cstore_mem_alloc.cpp
)
/** @Description: 初始化內存指針管理器*/
void CStoreMemAlloc::Init()
{// 重置計數器m_count = 0;// 斷言,確保 MaxPointersArryLen 是2的冪Assert((MaxPointersArryLen & (MaxPointersArryLen - 1)) == 0);// 初始化 m_tab 數組for (uint32 i = 0; i < MaxPointersArryLen; ++i) {m_tab[i].header = NULL;m_tab[i].tail = NULL;}// 初始化緩存節點計數器m_ptrNodeCacheCount = 0;
}
總結
CStoreMemAlloc 類
作用: CStoreMemAlloc 類用于管理列存儲中的內存分配和釋放。
功能:
- Palloc 方法: 分配內存,并根據需要注冊到管理器。
- FreePointerNode 方法: 釋放緩存的節點。
- Repalloc 方法: 重新分配內存,同時處理注冊和取消注冊。
- Pfree 方法: 釋放內存,同時處理取消注冊。
- Register 方法: 注冊內存指針,將其添加到管理表。
- Unregister 方法: 取消注冊內存指針,將其從管理表中移除。
- Reset 方法: 重置管理器狀態,釋放所有注冊的內存。
- Init 方法: 初始化管理器狀態。