openGauss 内存问题常用定位方法
openGauss 内存相关功能
内存上下文
openGauss通过内存上下文功能,来进行内存的管理,控制着内存的申请、释放、作用范围、生命周期等。
主要的思想是为不同的逻辑单元预申请专属的内存池,实现内存与逻辑单元的生命周期互相绑定,逻辑结束,内存释放的功能。在此之上又可以开发出更多的内存使用监控等功能。
内存上下文的主要概念有:chunk、block、freelist等。
block:直接在操作系统申请而来的内存大块,多个块之间串联成双链表。
chunk:在block中划分出来的内存小块,其大小共11个固定档次。结构可分为四部分,头、体、尾、无用空间。其中头内存放了所处的内存上下文、chunk大小等信息;体则是供我们使用的空间;尾部存储了一些校验单元,release下没有这部分;无用空间则是我们申请之外的空间,由于chunk大小时11档固定的,例如我们申请42字节时,会返回一个64字节档位的chunk,因此后面会有一部分无用空间。
freelist:共11个队列,每个队列都是chunk组成的双链表,一个队列中的chunk大小都是一样的。palloc、pfree都在这里取或还内存。
gs_shared_memory_detail
gs_session_memory_detail
gs_thread_memory_context
DBE_PERF.track_memory_context_derail(): 查看正在追踪的上下文的细节。
heap-use-after-free。内存释放后继续被使用。报告会给出内存释放后又被使用的堆栈、释放内存的堆栈、分配内存的堆栈。
heap-buffer-overflow。堆内存访问越界。如申请了10个字节的char数数组,通过下标直接访问第10个元素a[10]。报告会给出分配内存的堆栈、访问越界时的堆栈。
stack-buffer-overflow。栈内存访问越界。
global-buffer-overflow:全局内存访问越界。
double-free:重复释放同一块内存。报告会给出两次释放内存的堆栈、分配内存的堆栈。
根据堆栈走读代码,结合可复现的用例,单步gdb调试,方便随时打印相关变量的地址等信息,更容易分析定位。
不仅是创建的内存上下文,还有一些hash表等结构,是否使用了共享的版本,但实际非共享。
创建内存上下文或时,会指定所挂的parent,若此时创建了共享内存上下文,但挂在了非共享的内存上下文上面,则很容易出问题。
除非大的设计上的需求,否则不允许创建内存上下文时指定parent为NULL,创建不被管控的上下文。不然极易出现泄露的各种问题。
十一个档,暴力尝试当前chunk的长度,向后偏移指针,找到下一个chunk的头,进而找到memory context,进行下一步分析。
梳理代码,找到同一个上下文的其他还健在的变量,查看它的chunkheader,找到memory context进行下一步分析。
分析memory context也是无奈之举,通过查看block的地址范围,找到chunk所在的block,之后可以列出此block中的chunk,找到前一个完整的chunk。
根据梳理代码,大海捞针这个内存上下文所属的代码逻辑中,可能用到的数据结构,这个出问题的trunk的内容,如尝试尝试字符串、NodeTag等,或者本地搭建环境作为对照。
当然即使找到了前一个chunk,也不一定有用,因为也说不定已经被释放了,甚至重新被别人申请了。