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 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
