写点什么

AntDB 内存管理之内存上下文

  • 2022-11-02
    浙江
  • 本文字数:3527 字

    阅读完需:约 12 分钟

1.主题说明


AntDB 的内存管理在开发时,使用了内存上下文机制来实现内存管理。本文就从 AntDB 的内存上下文机制出发,解析内存上下文的实现原理。


AntDB 的代码中,涉及到内存的处理时,经常会看到下面这样的代码。

图 1:切换内存上下文示例


以及图 2 所示的代码。

图 2:在内存上下文中申请内存


这与平常开发 C/C++程序的内存操作方式不太一样,多多少少会让人产生一些疑惑

内存操作前后的 MemoryContextSwitchTo 是什么意思?

这个内存上下文为什么要切来切去的?什么时候需要切换?

palloc 函数只是调了一个函数指针,实际上由什么函数来实现的?

为什么经常只能看到内存申请操作,却看不到释放操作?内存申请到哪去了?不释放没问题吗?


接下来就为各位小伙伴慢慢解析一下这个内存上下文。


2.内存上下文(MemoryContext)是什么


内存上下文是一种内存管理机制。通俗一点来说,内存上下文可以看作是内存块和操作该内存块的方法的一个集合。举个例子,有一种内存上下文叫 MemoryContextA,如果用户切换到 MemoryContextA 的话,那么接下来的操作至少会遵循以下 2 个规则。


1)申请的内存都是属于 MemoryContextA,并且这些内存也会随着 MemoryContextA 的删除而被删除掉。


2)操作内存(申请、释放、重分配等等)都是由 MemoryContextA 定义的方法来执行的。


AntDB 中存在很多内存上下文,在它们之间会建立如图 3 所示的树形关系。(同一层之间其实还有兄弟关系,本图为了突出树形的层级关系,未在图中标识出兄弟关系。)

图 3:内存上下文树形关系


3.为什么要引入内存上下文


C/C++程序的开发者对内存操作这一部分(特别是内存的申请/释放操作)肯定深有体会,其中内存泄漏的问题更是家常便饭。尤其是当我们使用传统的内存操作方式来开发一个大型的软件(系统)时,保证内存操作不会出问题是相当有挑战的,并且这也会造成更多的开发成本。


AntDB 引入内存上下文机制后,可以使得我们不用在意每一处内存的申请/释放,也让内存管理工作变得更加清晰、方便、可靠。


4.内存上下文机制是怎么实现的


下文将针对内存上下文机制进行代码说明。本次以 AntDB 的代码为例,来解析内存上下文的实现方式。


4.1 最基础的数据结构


MemoryContextData 和 MemoryContextMethods 是内存上下文机制里最基础的 2 个数据结构。定义如下图 4 所示(只针对特定成员进行说明,其他的变量说明可以参照代码)。

图 4:MemoryContextData 和 MemoryContextMethods


※1:这个结构体只是个指针的集合而已,并没有实现。开发者可以根据这个框架自己提供一套实现方式。AntDB 已经实装的一套通用的实现:AllocSetMethods。另外 AntDB 也提供了 GenerationMethods 和 SlabMethods 的实现方式,但这 2 个需要在特定的使用场景下进行使用。接下来的说明都是以 AllocSetMethods 为前提的。


4.2 通用的实现 AllocSetContext


AntDB 提供了一个通用的内存上下文实现:AllocSetContext。代码中的内存操作几乎都是通过这个类型的内存上下文来处理的。


首先,我们了解一下 AllocSetContext 的定义,如下图 5 所示。

图 5:AllocSetContext


4.2.1 内存操作方法


其次,我们看一下 header 成员。从图 5 可以看出来,内存操作相关的方法都存放在了 header 成员里;另外,构建内存上下文的树形关系用的成员变量,比如 parent,firstchild 等,也都在 header 里面。


内存上下文可以提供的内存操作有以下几种。

AllocSetAlloc:内存申请

AllocSetFree:内存释放

AllocSetRealloc:内存重分配

AllocSetReset:内存上下文重置

AllocSetDelete:内存上下文删除

AllocSetGetChunkSpace:检查内存片的大小

AllocSetIsEmpty:检查内存上下文是否为空

AllocSetStats:获取内存上下文的状态信息

AllocSetCheck:检查所有内存


4.2.2 内存块


接下来,我们就了解一下内存上下文的实际内存在哪里。

图 6:内存上下文的实际内存块


blocks 成员是一个链表,内存上下文的内存都放在 blocks 成员里。内存上下文申请内存时是以 block(也可称作大内存)的方式,一次申请一块大内存。申请的 block 会加入到 AllocSetContext 的 blocks 链表中。


内存上下文提供的内存申请函数 AllocSetAlloc,是以 chunk(内存片,也可称作小内存)的方式分配内存给使用者。即该操作会把 block 根据需要分成一片一片,然后把内存片的地址提供给调用者。我们调用函数 palloc 得到的就是这个内存片的地址。


4.2.3 内存上下文实例的整体结构


上文已经分别介绍完了内存上下文的 2 个重要成员 header 和 blocks。据此,我们可以在脑中画出一个内存上下文实例大概的全貌。请参照下图 7。

图 7:内存上下文实例的整体结构


前面树形图中(图 3)看到的 TopMemoryContext、ErrorContext 等,从内存角度看的话,就是图 7 所显示的模样。


5.如何使用内存上下文


使用内存上下文之前,我们需要先对其进行创建。AntDB 启动时已经创建并初始化好了部分内存上下文,例如:TopMemoryContext。这个 TopMemoryContext 是所有内存上下文的父节点或者祖先节点。一般我们创建的内存上下文都在 TopMemoryContext 的子层以下。创建完之后,我们便可以通过 palloc/palloc0 使用该内存上下文,且使用完成之后可以释放内存上下文。


5.1 创建内存上下文


我们通过 AllocSetContextCreate 来创建内存上下文,这是一个宏定义。实际处理是由 AllocSetContextCreateInternal 来完成的。

图 8:AllocSetContextCreateInternal 函数定义


・parent:我们需要指定父节点的内存上下文。根据程序适当的设定。


・name:内存上下文的名称。


・minContextSize:内存上下文的最小尺寸。


・initBlockSize:内存上下文的初始尺寸。


・maxBlockSize:内存上下文的最大尺寸。


最后我们需调用 MemoryContextCreate 函数创建内存上下文。


5.2 在内存上下文中使用内存


在申请内存之前我们需要考虑:当前内存应该在哪一个内存上下文申请。不同的内存上下文,使用目的、生命周期都是不一样的。决定好内存上下文之后,我们可调用 MemoryContextSwitchTo 函数切换至目标内存上下文。


MemoryContextSwitchTo 函数的作用是切换至目标上下文,并返回当前的内存上下文。一般使用方法请参照:


1)当前内存上下文 = MemoryContextSwitchTo(目标内存上下文)。

2)进行内存申请等操作。这时候申请的内存都是在目标上下文中。

3)根据自己的代码处理逻辑。如果有需要,请及时切换回当前内存上下文。MemoryContextSwitchTo(当前内存上下文)。


内存上下文的切换不当,或者切换不及时的话,都可能会带来预料之外的后果,甚至直接导致程序崩溃。举个很简单的例子说明一下,如图 9 所示。

图 9:内存上下文切换不当示例


一旦决定好所需要用的内存上下文之后,我们就可以调用内存分配函数 palloc/palloc0 来申请内存了。注意一点,palloc0 会在申请完内存之后把内存全部初始化成 0,而 palloc 申请的内存的内容是不确定的。这两个函数的最终都是由 AllocSetAlloc 来实现。


内存使用完之后,可以调用 pfree 来释放。这个函数指向的是 AllocSetFree 函数。在 AntDB 中,palloc 的内存也并不是一定要调用 pfree 来释放内存。内存的释放工作可以留到内存上下文的释放阶段执行。


当然还有其他内存相关的各种操作,realloc、reset 等等。它们指向的函数在前面的 4.2.1 章节都已列出来了。


5.3 释放内存上下文


我们可以调用 MemoryContextDelete 来删除不再使用的内存上下文。如果仅仅是想释放内存上下文中的某些内存片的话,可以调用 pfree 来释放部分内存。注意一点,这个 pfree 操作只是把内存还给内存上下文,并不是还给操作系统。AntDB 还提供了一个内存重置 reset 的功能,这个 reset 可以释放所有内存块(除了特别设置成保留的内存块,保留内存块内容会被清除)。


6.总结


本篇文章给大家简单地介绍了内存上下文的基本概念,希望大家阅读之后对内存上下文有些基本的了解,以后再看到类似的代码不会过于陌生。同时,对本文一开始提出的几点疑惑,大家心中应该也找到了答案。


当然,本文只介绍了内存上下文的部分内容,还有很多的知识没有在文中进行阐述,比如:结构体中其他的成员变量干什么的;创建内存上下文是个什么流程;申请内存是个什么流程,有什么算法等等。还有,内存上下文有什么坑;有什么参数能控制内存上下文大小吗;AntDB 对内存上下文的持续改进……我们会在接下来的文章里继续和大家慢慢分享。先有概念,再有细节。本文就是建立概念的过程,来为后面讲解细节奠定基础。


最后,给各位数据库爱好者、技术爱好者一点建议。技术不是通过阅读一些技术文章就能提升的,我们需要自己动手实验、调试,才能把看到的内容转化成自己的知识。欢迎大家关注我们 AntDB,跟我们一起在数据库的世界里共同探索,一起成长。


关于 AntDB 数据库


AntDB 数据库始于 2008 年,在运营商的核心系统上,为全国 24 个省份的 10 亿多用户提供在线服务,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行近十年,并在通信、金融、交通、能源、物联网等行业成功商用落地。

用户头像

企业数据库创新实践者 2021-07-26 加入

AntDB数据库始于2008年,服务于全国20多个省份的10亿多用户提供在线服务;具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔电信核心交易,并保障系统持续0故障运行近十年。 官网:asiainfoah.com

评论

发布
暂无评论
AntDB内存管理之内存上下文_aisware antdb_亚信AntDB数据库_InfoQ写作社区