postgresql之内存池-AllocsetContext
一、简介
postgresql大部分的内存分配管理都是通过MemoryContext进行操作的,
多个相关的MemoryContext构成了一个树型结构,
多个树构成了一个森林。
实现了三种MemoryContext:
- SlabContext
- GenerationContext
- AllocSetContext
使用全局变量CurrentMemoryContext进行MemoryContext切换
二、编程范式
2.1 创建MemoryContext
newcontext = AllocSetContextCreate((MemoryContext) NULL,"TestMemoryContext",ALLOCSET_DEFAULT_SIZES);
2.2 切换需要使用的MemoryContext
MemoryContextSwitchTo(newcontext);
2.3 使用相应API进行分配/释放空间
//分配空间
item = (text *) palloc(VARHDRSZ + len + 1);
//分配空间,并且清空
plabel = palloc0(sizeof(pending_label));
//扩容/缩容
es->steps = repalloc(es->steps, sizeof(ExprEvalStep) * es->steps_alloc);
//释放空间
pfree(r);
2.4 重置MemoryContext
MemoryContextReset(argContext);
2.5 销毁MemoryContext
MemoryContextDelete(tmpCxt);
三、实现原理
3.1 数据结构简介
AllocSetContext由三部分构成, MemoryContextData + AllocBlock + AllocChunk
MemoryContextData:
- 由如下字段将各个MemoryContext构成树型结构
MemoryContext parent; /* NULL if no parent (toplevel context) */
MemoryContext firstchild; /* head of linked list of children */
MemoryContext prevchild; /* previous child of same parent */
MemoryContext nextchild; /* next child of same parent */
- 虚函数表,实现统一接口,不同实现
const MemoryContextMethods *methods; /* virtual function table */
AllocBlock:
- 由如下字段将block构成一个双向链表
AllocBlock prev; /* prev block in aset's blocks list, if any */
AllocBlock next; /* next block in aset's blocks list, if any */
- 标识可用空间范围
char *freeptr; /* start of free space in this block */
char *endptr; /* end of space in this block */
AllocChunk:
- chunk中可用大小
Size size;
3.2 初始化
#define AllocSetContextCreate \AllocSetContextCreateInternal#define ALLOCSET_DEFAULT_MINSIZE 0
#define ALLOCSET_DEFAULT_INITSIZE (8 * 1024)
#define ALLOCSET_DEFAULT_MAXSIZE (8 * 1024 * 1024)
#define ALLOCSET_DEFAULT_SIZES \ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZETopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,"TopMemoryContext",ALLOCSET_DEFAULT_SIZES);
MemoryContext
AllocSetContextCreateInternal(MemoryContext parent,const char *name,Size minContextSize,Size initBlockSize,Size maxBlockSize)
{int freeListIndex;Size firstBlockSize;AllocSet set;AllocBlock block;
.../* Determine size of initial block */firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;if (minContextSize != 0)firstBlockSize = Max(firstBlockSize, minContextSize);elsefirstBlockSize = Max(firstBlockSize, initBlockSize);/** Allocate the initial block. Unlike other aset.c blocks, it starts with* the context header and its block header follows that.*/set = (AllocSet) malloc(firstBlockSize);.../* Fill in the initial block's block header */block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext)));block->aset = set;block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;block->endptr = ((char *) set) + firstBlockSize;block->prev = NULL;block->next = NULL;
.../* Remember block as part of block list */set->blocks = block;/* Mark block as not to be released at reset time */set->keeper = block;...set->initBlockSize = initBlockSize;set->maxBlockSize = maxBlockSize;set->nextBlockSize = initBlockSize;set->freeListIndex = freeListIndex;
...set->allocChunkLimit = ALLOC_CHUNK_LIMIT;while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) >(Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION))set->allocChunkLimit >>= 1;/* Finally, do the type-independent part of context creation */MemoryContextCreate((MemoryContext) set,T_AllocSetContext,&AllocSetMethods,parent,name);((MemoryContext) set)->mem_allocated = firstBlockSize;return (MemoryContext) set;
}

3.3 分配空间
void *
palloc(Size size)
{/* duplicates MemoryContextAlloc to avoid increased overhead */void *ret;MemoryContext context = CurrentMemoryContext;
...context->isReset = false;ret = context->methods->alloc(context, size);
...return ret;
}
3.3.1 分配空间大于allocChunkLimit
static void *
AllocSetAlloc(MemoryContext context, Size size)
{AllocSet set = (AllocSet) context;AllocBlock block;AllocChunk chunk;int fidx;Size chunk_size;Size blksize;.../** If requested size exceeds maximum for chunks, allocate an entire block* for this request.*/if (size > set->allocChunkLimit){chunk_size = MAXALIGN(size);blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;block = (AllocBlock) malloc(blksize);if (block == NULL)return NULL;context->mem_allocated += blksize;block->aset = set;block->freeptr = block->endptr = ((char *) block) + blksize;chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);chunk->aset = set;chunk->size = chunk_size;/** Stick the new block underneath the active allocation block, if any,* so that we don't lose the use of the space remaining therein.*/if (set->blocks != NULL){block->prev = set->blocks;block->next = set->blocks->next;if (block->next)block->next->prev = block;set->blocks->next = block;}else{block->prev = NULL;block->next = NULL;set->blocks = block;}return AllocChunkGetPointer(chunk);}
...
}
例如分配16K
char *ptr = palloc(16 * 1024);

例如分配32K
char *ptr = palloc(32 * 1024);

3.3.2 分配空间小于等于allocChunkLimit
#define ALLOC_MINBITS 3 /* smallest chunk size is 8 bytes */static inline int
AllocSetFreeIndex(Size size)
{int idx;if (size > (1 << ALLOC_MINBITS)){
...idx = 31 - __builtin_clz((uint32) size - 1) - ALLOC_MINBITS + 1;
...}elseidx = 0;return idx;
}
fidx = AllocSetFreeIndex(size);/** Choose the actual chunk size to allocate.*/
chunk_size = (1 << ALLOC_MINBITS) << fidx;
-
分配小于等于8字节
小于等于8的,最终将分配8字节 -
分配大于8字节
通过__builtin_clz计算size-1的前导0的个数,然后通过减法计算size的对其位数(非常高效的方式)
3.3.2.1 当前block有足够空间
if ((block = set->blocks) != NULL)
{
...
}
/** OK, do the allocation*/chunk = (AllocChunk) (block->freeptr);
...block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
...chunk->aset = (void *) set;chunk->size = chunk_size;
...return AllocChunkGetPointer(chunk);
例如分配20byte
char *ptr = palloc(20);
通过对其,实际分配32byte

3.3.2.2 当前block没有足够的空间
随着分配,当前block中的可用空间越来越小,再次分配时,将不够当前分配
Size availspace = block->endptr - block->freeptr;
if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ))
{
...
}

3.3.2.2.1 当前block还有一些空间
if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ)){while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ)){Size availchunk = availspace - ALLOC_CHUNKHDRSZ;int a_fidx = AllocSetFreeIndex(availchunk);...if (availchunk != ((Size) 1 << (a_fidx + ALLOC_MINBITS))){a_fidx--;Assert(a_fidx >= 0);availchunk = ((Size) 1 << (a_fidx + ALLOC_MINBITS));}chunk = (AllocChunk) (block->freeptr);
...block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);availspace -= (availchunk + ALLOC_CHUNKHDRSZ);chunk->size = availchunk;chunk->aset = (void *) set->freelist[a_fidx];set->freelist[a_fidx] = chunk;}/* Mark that we need to create a new block */block = NULL;}
将剩余空间大小,按照对其的大小放到freelist中,构成一个单链表,用于后续分配时,直接从这里获取。
可能会有一小块用不了的空间

3.3.2.2.2 创建新block
- 第一次创建时,使用initBlockSize
- 后续将会是上次block的两倍,当大于maxBlockSize,使用maxBlockSize
- 如果新block大小依然小于需要分配的空间,则继续扩大
/** Time to create a new regular (multi-chunk) block?*/if (block == NULL){Size required_size;/** The first such block has size initBlockSize, and we double the* space in each succeeding block, but not more than maxBlockSize.*/blksize = set->nextBlockSize;set->nextBlockSize <<= 1;if (set->nextBlockSize > set->maxBlockSize)set->nextBlockSize = set->maxBlockSize;/** If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more* space... but try to keep it a power of 2.*/required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;while (blksize < required_size)blksize <<= 1;/* Try to allocate it */block = (AllocBlock) malloc(blksize);
...if (block == NULL)return NULL;context->mem_allocated += blksize;block->aset = set;block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;block->endptr = ((char *) block) + blksize;/* Mark unallocated space NOACCESS. */VALGRIND_MAKE_MEM_NOACCESS(block->freeptr,blksize - ALLOC_BLOCKHDRSZ);block->prev = NULL;block->next = set->blocks;if (block->next)block->next->prev = block;set->blocks = block;}


3.3.3 从freelist中分配
fidx = AllocSetFreeIndex(size);
chunk = set->freelist[fidx];
if (chunk != NULL)
{set->freelist[fidx] = (AllocChunk) chunk->aset;chunk->aset = (void *) set;
...return AllocChunkGetPointer(chunk);
}

3.4 释放空间
void
pfree(void *pointer)
{MemoryContext context = GetMemoryChunkContext(pointer);context->methods->free_p(context, pointer);
...
}
3.4.1 直接释放
static void
AllocSetFree(MemoryContext context, void *pointer)
{AllocSet set = (AllocSet) context;AllocChunk chunk = AllocPointerGetChunk(pointer);
...if (chunk->size > set->allocChunkLimit){AllocBlock block = (AllocBlock) (((char *) chunk) - ALLOC_BLOCKHDRSZ);.../* OK, remove block from aset's list and free it */if (block->prev)block->prev->next = block->next;elseset->blocks = block->next;if (block->next)block->next->prev = block->prev;context->mem_allocated -= block->endptr - ((char *) block);
...free(block);}else{...}
}
见 3.3.1, 大于chunklimit的直接malloc的空间,而不是从block中分配的,因此直接释放,并且将此block从双向链表中删除。

3.4.2 加入freelist中
static void
AllocSetFree(MemoryContext context, void *pointer)
{AllocSet set = (AllocSet) context;AllocChunk chunk = AllocPointerGetChunk(pointer);/* Allow access to private part of chunk header. */VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOCCHUNK_PRIVATE_LEN);#ifdef MEMORY_CONTEXT_CHECKING/* Test for someone scribbling on unused space in chunk */if (chunk->requested_size < chunk->size)if (!sentinel_ok(pointer, chunk->requested_size))elog(WARNING, "detected write past chunk end in %s %p",set->header.name, chunk);
#endifif (chunk->size > set->allocChunkLimit){...}else{/* Normal case, put the chunk into appropriate freelist */int fidx = AllocSetFreeIndex(chunk->size);chunk->aset = (void *) set->freelist[fidx];
...set->freelist[fidx] = chunk;}
}

相关文章:
postgresql之内存池-AllocsetContext
一、简介 postgresql大部分的内存分配管理都是通过MemoryContext进行操作的, 多个相关的MemoryContext构成了一个树型结构, 多个树构成了一个森林。 实现了三种MemoryContext: SlabContextGenerationContextAllocSetContext 使用全局变量CurrentMemo…...
QT 使用单例模式
目录 1. 单例模式介绍 2.单例模式实现 1. 单例模式介绍 有些时候我们在做 qt 项目的时候,要用到很多类. 例如我们用到的类有 A,B,C,D. 其中,A 是 B,C,D 中都需要用到的类,A 类非常的抢手. 但是,A 类非常的占内存,定义一个 A 对象需要 500M 内存,假如在 B,C,D 中都定义一个 A 类…...
接口测试——postman接口测试(三)
目录 1. postman介绍与安装 2. postman发送get请求 3. postman发送post请求 1. postman介绍与安装 安装网址:Postman安装教程:留言找我要即可 2. postman发送get请求 import pymysql from flask import Flask,request# 这里是mysql的基本连接信息 c…...
react中hooks的理解与使用
一、作用 我们知道react组件有两种写法一种是类组件,另一种是函数组件。而函数组件是无状态组件,如果我们要想改变组件中的状态就无法实现了。为此,在react16.8版本后官方推出hooks,用于函数组件更改状态。 二、常用API 1、use…...
STM32的电动自行车信息采集上报系统(学习)
摘要 针对电动自行车实时监管不便的问题,设计了一种基于STM32的电动自行车信息采集系统,通过获取电池、位置和行驶状态信息并上报到服务器中,实现实时监管。 通过多路串口请求电池、行驶状态和位置信息,以并发方式进行数据接收、…...
蓝桥杯上岸每日N题 第七期(小猫爬山)!!!
蓝桥杯上岸每日N题 第七期(小猫爬山)!!! 同步收录 👇 蓝桥杯上岸必背!!!(第四期DFS) 大家好 我是寸铁💪 冲刺蓝桥杯省一模板大全来啦 🔥 蓝桥杯4月8号就要开始了 &a…...
【Linux系统编程】冯诺依曼体系结构
目录 前言 什么是冯诺依曼体系结构? 冯诺依曼体系结构如何进行数据处理的? 存储器在冯诺依曼体系中有什么作用? 冯诺依曼体系结构为什么要这样设计? 冯诺依曼结构总结 前言 相信对于冯诺依曼这个人的名字大家一定不会感到陌…...
数据结构--动态顺序表
文章目录 线性表动态顺序表数组与顺序表 接口实现初始化:尾插:尾删头插头删指定位置插入指定位置删除查找摧毁 完整代码 线性表 线性表是数据结构中最基本、最简单也是最常用的一种数据结构。线性表是指由n个具有相同数据类型的元素组成的有限序列。 线…...
笔试数据结构选填题
目录 卡特兰数Catalan:出栈序列/二叉树数 树 二叉树 N01N2 哈夫曼树(最优二叉树)Huffman 度m的哈夫曼树只有度为0和m的结点:Nm(n-1)/(m-1) 平衡二叉树AVL Nh表示深度为h最少结点数,则N00,N11&#…...
# 鸢尾花的案例学习
# 鸢尾花的案例学习 # 1. 导入小型的数据 from sklearn.datasets import load_iris import numpy as np import pandas as pd import seaborn as sbn import matplotlib.pyplot as plt # 2. 获取数据 irisload_iris() # 3.查看数据print("数据集\n ",len(iris.d…...
线程、进程的区别
线程、进程的区别 在开发中,我们经常听到线程和进程两个概念,它们都是操作系统的基本概念,操作系统以进程为基本单位分配存储器,以线程为基本单位分配CPU。虽然它们有很多相似之处,但是它们也有很大的区别。本文将详细…...
在 Ubuntu 上安装 Docker 桌面
Ubuntu 22.04 (LTS) 安装 Docker 桌面 要成功安装 Docker Desktop,您必须: 满足系统要求拥有 64 位版本的 Ubuntu Jammy Jellyfish 22.04 (LTS) 或 Ubuntu Impish Indri 21.10。对于非 Gnome 桌面环境,必须安装 gnome-terminal:…...
【WebRTC---序篇】(七)RTC多人连麦方案
服务端可以选择mediasoup,作为SFU服务器,只负责转发数据 下图举例三个Client (browser或者客户端)同时加入一个房间,每个app同时发布一路视频和一路音频,并且接受来自其他app的音视频流,mediasoup内部的结构如下&…...
【Java可执行命令】(十六)诊断命令请求发送工具 jcmd:提供一种简单而强大的方式来管理和监控 Java 进程 ~
Java可执行命令之jcmd 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式3.2 jcmd -l:列出正在运行的 Java 进程3.3 jcmd < pid> help:列出特定进程的诊断命令列表3.4 jcmd < pid> < command>:执行诊断命令 4️⃣ 应用场景…...
如何创建无序列表和有序列表?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 无序列表⭐ 无序列表⭐ 注意事项⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴…...
【MongoDB】初识、安装MongoDB
目录 一、MongoDB主要应用场景 二、MongoDB简介 三、MongoDB相关特点 四、MongoDB的安装 一、MongoDB主要应用场景 传统的数据库如MySQL在应对三高场景时显得力不从心 三高: High performance 对数据库高并发读写的需求 High Storage 对海量数据的高效率存储和 …...
方法区内存溢出及常量池
22 方法区-定义 是所有线程共享的一块区域。 存储了和类结构相关信息。运行时常量池, 方法区在虚拟机启动时被创建,逻辑上是堆的组成部分。方法区内存不足,也会导致oom异常。 是一个概念上的东西, 1.6使用永久代作为方法区&#…...
【MTK平台】【wpa_supplicant】关于wpa_supplicant_8/src/p2p/p2p_invitation.c文件的介绍
本文主要介绍external/wpa_supplicant_8/src/p2p/p2p_invitation.c文件 这里主要介绍6个方法 1.p2p_invite //p2p邀请调用此方法 2.p2p_invite_send //对p2p_invite方法进行补充 3. p2p_process_invitation_resp 4.p2p_process_invitation_req 5.p2p_build_invitation_re…...
智能仪表板DevExpress Dashboard v23.1亮点 - 增强对自定义导出的支持
DevExpress Dashboard v23.1版本增强了自定义导出到Excel的功能等,欢迎下载最新版本体验! DevExpress Dashboard v23.1正式版下载(Q技术交流:523159565) 所有平台 导出自定义仪表板项目到Excel 用户现在可以在WinForms和Web应…...
分布式应用:ELK企业级日志分析系统
目录 一、理论 1.ELK 2.ELK场景 3.完整日志系统基本特征 4.ELK 的工作原理 5.ELK集群准备 6.Elasticsearch部署(在Node1、Node2节点上操作) 7.Logstash 部署(在 Apache 节点上操作) 8.Kiabana 部署(在 Node1 节点…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
FOPLP vs CoWoS
以下是 FOPLP(Fan-out panel-level packaging 扇出型面板级封装)与 CoWoS(Chip on Wafer on Substrate)两种先进封装技术的详细对比分析,涵盖技术原理、性能、成本、应用场景及市场趋势等维度: 一、技术原…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
初级程序员入门指南
初级程序员入门指南 在数字化浪潮中,编程已然成为极具价值的技能。对于渴望踏入程序员行列的新手而言,明晰入门路径与必备知识是开启征程的关键。本文将为初级程序员提供全面的入门指引。 一、明确学习方向 (一)编程语言抉择 编…...
轻量安全的密码管理工具Vaultwarden
一、Vaultwarden概述 Vaultwarden主要作用是提供一个自托管的密码管理器服务。它是Bitwarden密码管理器的第三方轻量版,由国外开发者在Bitwarden的基础上,采用Rust语言重写而成。 (一)Vaultwarden镜像的作用及特点 轻量级与高性…...
