OceanBase内存管理小窍门
本文来自OceanBase热心用户的实践分享。
本文主要是对OceanBase内存管理的实用技巧分享,而并非直接深入OceanBase的代码层面进行阐述。
阅读本文章你将了解:
- 重载运算符new 与malloc在返回值上区别?
- 在ceph 双向链表新用法,一个类定义时候 成员变量就是包含了 双向链表节点,可以通过该节点反推 类其他变量吗?
- 在stl中 中如何利用单链表存储申请批量对象?从对象中拿出固定字节就就可充当单链表?
- ob ob_allocator.h 与stl ob_allocator.h 分配器实现 有什么差别?
内存管理
C++中通过new和delete两个关键字进行动态内存管理。 c语言通过 malloc 和free 两个关键字进行动态内存管理
函数支持重载,运算符同样也支持重载
C++的提供了 重载运算符这一特性, 本质也是operators()函数重载,当遇到该运算符时就调用函数一样。
运算符重载的限制


小提示:Markdown左对,在原来基础上,后面一个空格就解决了 右对齐HTML css语法
重载运算符new
throwing (1) void* operator new (std::size_t size);
// throwing allocation ,On failure, it throws a bad_alloc exceptionnothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
//nothrow allocation on failure it returns a null pointer instead of throwing an exceptionplacement (3) void* operator new (std::size_t size, void* ptr) noexcept;
//placement Simply returns ptr (no storage is allocated).
// A pointer to an already-allocated memory block
代码示例
MyClass * p1 = new MyClass();
// allocates memory by calling: operator new (sizeof(MyClass))
// and then constructs an object at the newly allocated spacestd::cout << "2: ";MyClass * p2 = new (std::nothrow) MyClass();
// allocates memory by calling: operator new (sizeof(MyClass),std::nothrow)
// and then constructs an object at the newly allocated spacestd::cout << "3: ";
new (p2) MyClass();//p2
delete p1;
delete p2;
malloc
https://en.cppreference.com/w/c/memory/malloc
void *malloc( size_t size );
Allocates size bytes of uninitialized storage,
alloc is thread-safeParameters
size - number of bytes to allocate
sizeof Queries size of the object or type.On failure, returns a null pointer.
ob代码:ob_alter_table_resolver.cpp
//申请批量内存时候使用,__MemoryContext__ *tmp = new (std::nothrow) __MemoryContext__();abort_unless(tmp != nullptr); //void *tmp_ptr = NULL;common::ObIAllocator *allocator_;//分配器if (NULL == (tmp_ptr = (ObAlterPrimaryArg *)allocator_->alloc(sizeof(obrpc::ObAlterPrimaryArg)))) {} else {alter_pk_arg = new (tmp_ptr) ObAlterPrimaryArg(); //这里没有使用delete}
重载new运算符 使用场景
- 批量申请内容时候,使用std::nothrow 不抛出异常,通过返回值判断nullptr 来处理
- C++ placement new与内存池有关系,能帮助更节省内存吗?不清楚继续看
有些时候我们需要能够长时间运行的程序(例如监听程序,服务器程序)对于这些7*24运行的程序,我们不应该使用标准库提供的new 和 delete (malloc和free也算)。这是因为随着程序的运行,内存不断的被申请和被释放,频繁的申请和释放将会引发内存碎片、内存不足等问题,影响程序的正常运行。
更多的时候核心程序不允许内存申请失败,更不允许异常的出现,因此必须保证每次内存申请都是成功的(一般都是内核程序,当然不希望被中断的后台程序也是如此)。在这种极端要求下,内存池的好处就大大的凸现出来了。
在C++中,可以通过placement new 来实现内存池
如果分配能节省内存
内存池是很大概念,我平时用不到,上来不会说明原理,这是自己给自己挖坑,自己不会还要去自己讲清楚 先看一段代码,你发现什么错误吗?
一般定义链表,都有T 成员表示,但是ceph 中 定义 elist为什么没有,它怎么存储数据呢?
class Node
{
public:int data; //存储数据Node * last;Node * next;};class DoubleNode
{
private:Node * head; //头结点Node * tail; //尾节点
};
一般定义链表,都有T 成员表示,但是elist为什么没有,它怎么存储数据呢?
完整代码:
https://lab.forgefriends.org/ceph/ceph/-/blob/wip-rgw-placement-rule-empty/src/include/elist.h
/** elist: embedded list. 这是一个双向链表,必须和类耦合起来。* elist(embedded list)是一种特殊类型的链表,它允许将链表节点直接嵌入到用户定义的数据结构中。这种设计使得每个数据项可以作为链表的一部分* requirements:* - elist<T>::item be embedded in the parent class 定义类时候,必须使用 elist<T>::item 当作一个成员* - items are _always_ added to the list via the same elist<T>::item at the same* fixed offset in the class. //items 在类中偏移量* - begin(), front(), back() methods take the member offset as an argument for traversal.**///计算成员变量在类中的偏移量
#define member_offset(cls, member) ((size_t)(&((cls*)1)->member) - 1)template<typename T>
class elist {
public:struct item {item *_prev, *_next;//通过偏移量T get_item(size_t offset) {ceph_assert(offset);return (T)(((char *)this) - offset); }}; //elist<T>::item 是作为用户定义结构体的成员变量存在的。//意味着 item 的内存是从用户结构体的内存中分配的,而不是独立分配。private:item _head;size_t item_offset;
}class iterator {private:item *head;item *cur, *next;size_t item_offset;public:T operator*() {return cur->get_item(item_offset);}
};
- c++ 内存模型 (了解)
GCC 或 Clang,你可以使用 __builtin_offsetof 函数来获取成员的偏移量:
#define member_offset(cls, member) ((size_t)(&((cls*)1)->member) - 1)
class Example {
public:char a; // 1 byteint b; // 4 bytes, aligned to 4 bytesdouble c; // 8 bytes, aligned to 8 bytesbool d; // 1 byte, but often padded to align with 'b'
};size_t offset_a = __builtin_offsetof(Example, a);__size_t offset_b = __builtin_offsetof(Example, b)能否提供一个完整的示例,展示如何在一个复杂的类中嵌入 `elist` 并使用它?https://kimi.moonshot.cn/share/cqqc6ga1n4gqsenn4ur0
https://kimi.moonshot.cn/share/cqqcdsdskq8g1pv5ces0
STL源码剖析 by 侯捷 提到一个同样技巧
资料:STL标准库与泛型编程
- what:关于STL中空间配置器中free_list的理解,理解不了_Obj 单链表将多个 对象组织起来?

union _Obj {union _Obj* _M_free_list_link; // 单链表char _M_client_data[1]; /* The client sees this. */}; 关于STL中空间配置器中free_list的理解
- how:参考资料
自己动手实现STL 01:内存配置器的实现(stl_alloc.h)
https://github.com/wangcy6/sgi-stl/blob/master/stl_alloc.h
https://www.cnblogs.com/wangjzh/p/4097355.htmlhttps://github.com/wangcy6/STLSourceCodeNote
第一级配置器malloc_alloc 就是,直接调用系统的malloc分配内存
//第一级配置器malloc_alloc 就是,直接调用系统的malloc分配内存
typedef __malloc_alloc_template<0> malloc_alloc;template <int __inst> //这个模板没啥意义,区分一级二级区别
class __malloc_alloc_template {
private:static void* _S_oom_malloc(size_t);static void* _S_oom_realloc(void*, size_t);
public:static void* allocate(size_t __n){void* __result = malloc(__n);if (0 == __result) //malloc是否返回0__result = _S_oom_malloc(__n); //分配失败继续分配return __result;}static void deallocate(void* __p, size_t /* __n */){free(__p);}
}
第二级配置器(Second-level allocator):。
default_alloc 尝试通过分配大块内存(称为 "chunks")来减少内存碎片,并使用这些大块内存来满足较小的内存请求。 它使用一个自由列表(free list)机制来管理这些大块内存中的小块内存。
default_alloc 可以是线程安全的,并且提供了更好的内存局部性和缓存性能。
//第二级配置器typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
template <bool threads, int inst>
class __default_alloc_template {union _Obj {union _Obj* _M_free_list_link;char _M_client_data[1]; /* The client sees this. */};
}_S_refill(size_t __n)
{// 定义分配的对象数量为20,这个值可以根据需要调整。int __nobjs = 20;// 调用 _S_chunk_alloc 函数分配足够存储 __nobjs 个大小为 __n 的对象的内存块。char* __chunk = _S_chunk_alloc(__n, __nobjs);// __my_free_list 指向适当大小的自由列表的指针。_Obj* __STL_VOLATILE* __my_free_list;// __result 指向新分配的内存块的起始位置,将被返回给调用者。_Obj* __result;// __current_obj 和 __next_obj 用于遍历和设置对象链表的指针。_Obj* __current_obj;_Obj* __next_obj;// __i 是循环计数器。int __i;// 如果只分配了一个对象,就直接返回这个对象的内存。if (1 == __nobjs) return(__chunk);// 计算并获取对应大小的自由列表。__my_free_list = _S_free_list + _S_freelist_index(__n);// 构建内存块内的自由链表。// __result 初始化为指向内存块的起始位置。__result = (_Obj*)__chunk;// 第一个对象之后的对象地址设置为自由链表的头。*__my_free_list = __next_obj = (_Obj*)(__chunk + __n);// 循环将内存块分割成多个对象,并用 _M_free_list_link 将它们链接起来。for (__i = 1; ; __i++) {// __current_obj 指向当前正在处理的对象。__current_obj = __next_obj;// 计算下一个对象的地址。__next_obj = (_Obj*)((char*)__next_obj + __n);// 如果这是分配的最后一个对象,将其 _M_free_list_link 设置为 NULL,结束链表。if (__nobjs - 1 == __i) {__current_obj -> _M_free_list_link = 0;break;} else {// 否则,将当前对象的 _M_free_list_link 设置为指向下一个对象。__current_obj -> _M_free_list_link = __next_obj;}}// 返回可以立即使用的首个对象的地址。return(__result);
}

OceanBase怎么做的
- 先看例子
ParseNode *key_child_node;key_child_node = static_cast<ParseNode*>(allocator.alloc(sizeof(ParseNode))) //key_child_node = new(key_child_node) ParseNode;oceanbase/deps/oblib/src/lib/allocator/ob_allocator.hclass ObAllocator : public ObIAllocator//直接看看发狂,概念太多,还是stl看着舒服//
- 参考:从0到1 OceanBase原生分布式数据库内核实战进阶版
从0到1 OceanBase原生分布式数据库内核实战进阶版

相关文章:
OceanBase内存管理小窍门
本文来自OceanBase热心用户的实践分享。 本文主要是对OceanBase内存管理的实用技巧分享,而并非直接深入OceanBase的代码层面进行阐述。 阅读本文章你将了解: 重载运算符new 与malloc在返回值上区别?在ceph 双向链表新用法&am…...
【问题解决】git status中文文件名乱码
问题复现 解决办法 在git bash中直接执行如下命令 git config --global core.quotepath false原因 通过 git config --help 可以查看到以下内容: core.quotePath Commands that output paths (e.g. ls-files, diff), will quote “unusual” characters in the p…...
探索数据结构:AVL树的分析与实现
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:数据结构与算法 贝蒂的主页:Betty’s blog 1. AVL树的介绍 在前面我们学习二叉搜索树时知道,在数据有序…...
使用 C++ 实现简单的插件系统
使用 C 实现简单的插件系统 在现代软件开发中,插件系统是一种常见的架构模式,它允许开发者在不修改主程序的情况下,扩展应用程序的功能。通过插件,用户可以根据需要添加或移除功能模块,从而提高软件的灵活性和可维护性…...
使用Python创建省份城市地图选择器
在这篇博客中,我们将探讨如何使用Python创建一个简单而实用的省份城市地图选择器。这个项目不仅能帮助我们学习Python的基础知识,还能让我们了解如何处理JSON数据和集成网页浏览器到桌面应用程序中。 C:\pythoncode\new\geographicgooglemap.py 全部代码…...
【Java 数据结构】Stack和Queue介绍
Stack和Queue StackStack是什么Stack的使用构造方法常用方法 栈的模拟实现初始化和基本方法入栈出栈查看栈顶 栈的应用链栈的简单介绍 QueueQueue是什么Queue的使用队列的模拟实现初始化入队出队查看队头元素 循环队列循环队列的定义及其注意点循环队列的实现初始化和基本方法获…...
Docker基本语法
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、更新yum镜像仓库(一)查看本地yum镜像源地址(二)设置docker的镜像仓库(1)安装必要工具…...
uniapp 对于scroll-view滑动和页面滑动的联动处理
需求 遇到一个需求 解决方案 这个时候可以做一个内页面滑动判断 <!-- scroll-y 做true或者false的判断是否滑动 --> <view class"u-menu-wrap" style"background-color: #fff;"><scroll-view :scroll-y"data.isGo" scroll-wit…...
opencv基础的图像操作
1.读取图像,显示图像,保存图像 #图像读取、显示与保存 import numpy as np import cv2 imgcv2.imread(./src/1.jpg) #读取 cv2.imshow("img",img) #显示 cv2.imwrite("./src/2.jpg",img) #保存 cv2.waitKey(0) #让程序进入主循环(让…...
Java | Leetcode Java题解之第337题打家劫舍III
题目: 题解: class Solution {public int rob(TreeNode root) {int[] rootStatus dfs(root);return Math.max(rootStatus[0], rootStatus[1]);}public int[] dfs(TreeNode node) {if (node null) {return new int[]{0, 0};}int[] l dfs(node.left);i…...
本地查看的Git远程仓库分支与远程仓库分支数量不一致
说明:一次,在IDEA中想切换到某分支,但是查看Remote没有找到要切换的分支,但是打开GitLab,查看远程仓库,是有这个分支的。 解决:1)在IDEA的Git中,点下面Fatch获取一下远程…...
opencv-python实战项目九:基于拉普拉斯金字塔的图像融合
文章目录 一,简介:二,拉普拉斯金字塔介绍:三,算法实现步骤3.1 构建融合拉普拉斯金字塔3.2 融合后的拉普拉斯金字塔复原: 四,整体代码实现:五,效果: 一&#x…...
浅谈JDK
JDK(Java Development Kit) JDK是Java开发工具包,是Java编程语言的核心软件开发工具。 JDK包含了一系列用于开发、编译和运行Java应用程序的工具和资源。其中包括: 1.Java编译器(javac):用于将Java源代码编译成字节…...
爬虫案例3——爬取彩票双色球数据
简介:个人学习分享,如有错误,欢迎批评指正 任务:从500彩票网中爬取双色球数据 目标网页地址:https://datachart.500.com/ssq/ 一、思路和过程 目标网页具体内容如下: 我们的任务是将上图中…...
C++ | Leetcode C++题解之第337题打家劫舍III
题目: 题解: struct SubtreeStatus {int selected;int notSelected; };class Solution { public:SubtreeStatus dfs(TreeNode* node) {if (!node) {return {0, 0};}auto l dfs(node->left);auto r dfs(node->right);int selected node->val…...
软件架构设计师-UML知识导图
软件架构设计师-UML知识导图,包含如下内容: 结构化设计,包含结构化设计的概念、结构化设计的主要内容、概要设计、详细设计及模块设计原则;UML是什么:介绍UML是什么;UML的结构:构造块、公共机制…...
在使用transformers和pytorch时出现的版本冲突的问题
在使用transformers和torch库的时候,出现了以下问题: 1、OSError: [WinError 126] 找不到指定的模块。 Error loading "D:\Program Files\anaconda3\envs\testenv\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies. 2、…...
uniapp粘贴板地址识别
1: 插件安装 主要是依靠 address-parse 这个插件: 官网 收货地址自动识别 支持pc、h5、微信小程序 - DCloud 插件市场 // 首先需要引入插件 npm install address-parse --save 2:html部分 <view class""><view class&quo…...
C语言 | Leetcode C语言题解之第335题路径交叉
题目: 题解: bool isSelfCrossing(int* distance, int distanceSize){if (distance NULL || distanceSize < 4) {return false;}for (int i 3; i < distanceSize; i) {if ((distance[i] > distance[i - 2]) && (distance[i - 1] &l…...
TypeScript学习第十三篇 - 泛型
在编译期间不确定变量的类型,在调用时,由开发者指定具体的类型。 1. 如何给arg参数和函数指定类型? function identity(arg){return arg; }identity(1) identity(jack) identity(true) identity([]) identity(null)定义的时候,无…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
