列存储(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 方法: 初始化管理器状态。