news 2026/4/16 13:43:15

Linux C/C++ 学习日记(51):内存池

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux C/C++ 学习日记(51):内存池

注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。

一、指针和内存大小对齐是什么?

1. 内存分配时的指针对齐(必须):也称为内存对齐

  • 结论:malloc/posix_memalign 等分配函数返回的指针,必须对齐(无例外)。
  • 核心依据
    ① 硬件底层:CPU 无法安全 / 高效访问非对齐地址(x86 性能暴跌,ARM 直接崩溃);
    ② C 语言标准强制要求,所有合规的内存分配函数都需满足。
  • 补充:默认对齐粒度(32 位系统 8 字节,64 位 16 字节),posix_memalign 可手动指定更大粒度(如内存池可用 32 / 64字节)。

2. 内存访问时的指针对齐(看场景)

  • 结论:访问指针不是 “绝对必须对齐”,取决于「数据类型 + CPU 架构」。
  • 核心依据
    • 访问 char(1 字节):任何地址都满足 1 字节对齐,无需额外要求;
    • 访问 int(4 字节)/double(8 字节):x86 非对齐可访问但性能暴跌(可能跨缓存行),ARM 非对齐直接崩溃;
    • 本质:对齐要求 = CPU “原生访问粒度”(如 char对应1字节,int 对应 4 字节,double 对应 8 字节)。

3. 内存大小对齐(非必须,纯优化)

  • 结论:内存大小无需强制对齐,仅高性能场景(内存池 / SIMD/DMA)需要。
  • 核心依据
    • 普通场景:malloc (4) 大小就是 4 字节,系统靠 “内部碎片” 管理,不影响访问;
    • 优化场景:大小对齐到缓存行(32/64 字节),能让内存块结束地址落在对齐边界,后续分配无碎片,且数据不跨缓存行。

4. 缓存行与内存访问的关系(性能补充)

  • 结论:缓存行是 “缓存↔内存” 的传输单位(32/64 字节),和 “指针 / 大小对齐” 是互补关系,而非冲突。
  • 核心依据
    ① CPU 访问内存时,先按缓存行批量加载数据到缓存(不管数据大小,比如读 1 字节 char 也会加载整行);
    ② 再从缓存按 “数据类型粒度”(如 char1 字节、int4 字节)读取到 CPU,对齐的指针能让这一步无额外拆分开销。

总结

  1. 分配指针:必须对齐(硬件 + 标准要求);
  2. 访问指针:char 随便,int/double 建议对齐(x86 慢、ARM 崩);
  3. 内存大小:不用对齐,高性能场景对齐到缓存行更优;
  4. 缓存行:批量加载数据的单位,对齐能让缓存访问无额外开销。

疑问:

问1:原生访问粒度是什么?

核心维度具体说明
定义CPU 从缓存 / 内存读取单个基础数据类型时,硬件层面的最小字节单位(由 CPU 架构 + 数据类型决定)。
本质CPU 指令操作数据的 “天然粒度”,是硬件级的访问规则,而非软件层面的优化。
典型示例(x86)char→1 字节、int→4 字节、double→8 字节(和数据类型的大小 / 对齐要求完全一致)。
关键关联访问指针需匹配对应数据类型的原生访问粒度(即对齐),否则 x86 性能可能暴跌、ARM 直接崩溃;

tips:对齐表示是整数倍的意思

问2:内存分配时的指针对齐,跟内存访问时的指针对齐规则不同:

对比维度内存分配时的指针对齐内存访问时的指针对齐
规则目标兜底性要求,保证指针适配所有数据类型的访问基础精准性要求,仅匹配当前访问的数据类型需求
对齐粒度统一粗粒度(32 位 8 字节 / 64 位 16 字节,或手动指定如 32 字节)按需细粒度(char=1 字节、int=4 字节、double=8 字节,随数据类型变化)
强制程度绝对强制(malloc/posix_memalign 等函数必满足,违反则分配失败 / 行为未定义)

非绝对强制(在x86中):

-char 无对齐限制;

-int/double 非对齐访问不导致程序崩溃:

比如x86中:
char arr[1024];
*(int*)(&arr[1]) = 5;

违规后果直接导致内存分配失败(如 posix_memalign 返回错误码)或指针不可用x86:仅性能暴跌(针对 int/double,可能存在跨行读取);
ARM:直接崩溃(针对 int/double);
char 无任何后果

问3:结构体中隐含的指针对齐

  • 成员级对齐:结构体每个成员按自身类型的对齐要求对齐(如 int→4 字节、double→8 字节),编译器会在成员间补 “填充字节”;
  • 整体级对齐:结构体总大小向上对齐到 “最大成员对齐数” 的整数倍,保证结构体数组访问时每个元素都对齐
#include <stdio.h> #include <stddef.h> // 用于offsetof宏验证偏移量 // 核心结构体:数组前/后都补填充,结构体末尾也补填充 struct AlignFullExample { // 成员1:char(触发数组前填充) char c; // 对齐数=1,占0x00(1字节) // 数组前补3字节填充(0x01~0x03)→ 让int数组满足4字节对齐 // 核心:int数组(前后都有填充) int arr[2]; // 对齐数=4,起始0x04,占0x04~0x0B(2×4=8字节,内部连续无填充) // 数组后补4字节填充(0x0C~0x0F)→ 让double满足8字节对齐 // 成员2:double(触发数组后填充) double d; // 对齐数=8,起始0x10,占0x10~0x17(8字节) // 成员3:char(触发结构体末尾填充) char e; // 对齐数=1,起始0x18,占0x18(1字节) // 结构体末尾补7字节填充(0x19~0x1F)→ 让总大小对齐到最大对齐数8 }; int main() { // 验证各成员的内存偏移量(起始地址) printf("=== 成员偏移量(字节)===\n"); printf("char c : %zu\n", offsetof(struct AlignFullExample, c)); printf("int arr[2] : %zu\n", offsetof(struct AlignFullExample, arr)); printf("double d : %zu\n", offsetof(struct AlignFullExample, d)); printf("char e : %zu\n", offsetof(struct AlignFullExample, e)); // 验证结构体总大小 printf("=== 结构体总大小 ===\n"); printf("AlignFullExample size: %zu 字节\n", sizeof(struct AlignFullExample)); return 0; }

sizeof(struct AlignFullExample )会得到 32 !

问4:CPU跨缓存行读取时什么意思?

当你要访问的数据跨越了两个缓存行的边界时,CPU 需要从内存加载两个缓存行才能拿到完整数据,这种情况就叫 “跨行读取”。

举例(缓存行 = 64 字节):

  • 缓存行 1 范围:0x1000 ~ 0x1040 (不含0x1040)
  • 缓存行 2 范围:0x1040 ~ 0x1080
  • 若要访问的数据是 “0x103C ~ 0x1043”(比如一个 8 字节的 double,0x103C 不对齐8字节:x86不会崩溃,ARM会崩溃),这个数据同时占了缓存行 1 和缓存行 2 → CPU 必须加载这两个缓存行,就是 “跨行读取”。
  • 若要访问的数据是 “0x103C ~ 0x1040”(比如一个 4 字节的 int,0x103C 对齐4字节:x86和ARM都不会崩溃),这个数据同时占了缓存行 1 和缓存行 2 → CPU 必须加载这两个缓存行,就是 “跨行读取”。
#include <stdio.h> #include <stdint.h> // 强制按64字节对齐(保证起始地址是64的整数倍,先锚定缓存行边界) __attribute__((aligned(64))) char buf[128]; int main() { // 1. 确定缓存行边界:buf起始地址=0x1000(64字节对齐), // 缓存行1:0x1000 ~ 0x1040,缓存行2:0x1040 ~ 0x1080 // 2. 取跨缓存行的对齐地址:0x103C(4字节对齐,满足int访问要求) uint32_t* ptr = (uint32_t*)(buf + 60); // buf+60 = 0x1000 + 60 = 0x103C // 3. 跨行读取:ptr指向的int(4字节)范围是0x103C ~ 0x1040, // 前3字节在缓存行1(0x103C~0x1040),最后1字节在缓存行2(0x1040) *ptr = 0x12345678; // 写入操作,触发跨行读取+写入 printf("写入值:0x%x\n", *ptr); // 读取验证 return 0; }

二、内存池是什么?

内存池(Memory Pool)是一种预分配 + 复用的内存管理机制:

  • 程序启动 / 初始化阶段,提前向操作系统申请一块连续的大块内存(内存池);
  • 后续程序需要分配内存时,直接从内存池中 “切割” 小块使用,无需频繁调用系统调用(如malloc/freenew/delete);
  • 内存释放时,并非立即归还给操作系统,而是放回内存池的空闲列表,供后续复用;
  • 当内存池耗尽时,可按需扩容(再次向系统申请大块内存);程序退出时,一次性释放整个内存池。

本质是将 “频繁的小内存系统调用” 转化为 “用户态的内存块复用”,核心目标是提升效率、减少碎片。

三、内存池解决了什么问题?

频繁使用malloc/free等原生内存分配方式会带来以下问题,内存池可针对性解决:

问题类型原生分配的痛点内存池的解决方案
内存碎片频繁分配 / 释放不同大小的内存块,会导致内存空间被分割成大量不连续的小碎片(外部碎片),即使总空闲内存足够,也无法分配大块连续内存预分配大块连续内存,内部按规则分割 / 复用,避免内存空间碎片化;定长池甚至可完全消除外部碎片
分配效率低malloc/free需要陷入内核态(系统调用),且需遍历空闲链表、计算分配位置,频繁调用开销极大内存池的分配 / 释放是用户态操作(仅操作空闲链表指针),无需内核态切换,效率提升 10~100 倍
缓存友好性差原生分配的内存地址随机,CPU 缓存命中率低预分配的连续内存更符合 CPU 缓存的局部性原理,提升内存访问速度
内存泄漏难排查原生分配的内存分散,难以跟踪分配 / 释放轨迹内存池可统一管理所有分配的内存块,便于统计、监控、排查泄漏
分配粒度失控原生分配可能因对齐、元数据占用导致实际分配内存大于需求(如分配 1B 实际占 8B)可定制化分配粒度(如定长池严格按固定大小分配),减少内存浪费

虚拟内存:

四、内存池的实现

(一)等长内存池

  • 频繁分配 / 释放固定大小的对象(如链表节点、线程池任务、网络连接对象),是最简单、最高效的内存池实现。
  • 每次分配相同大小的内存块

疑问:

问1:怎么知道下一块可用内存的地址?

内存块的前8/4个字节(指针大小)存放下一块可用的内存块起始地址。

*(char **)ptr = (char*)ptr + block_size;
问2:释放完的内存块如何再次使用?

释放完之后,将当前内存块的首部指向free_ptr,然后将free_ptr指向当前内存块

void memp_free(mempool_t *m, void *ptr) { *(char **)ptr = m->free_ptr; m->free_ptr = (char *)ptr; m->freecount++; }
问3:block_size 应该取多大?

答:依据内存对齐规则:取2的n次幂,最小取8。

(二)大小块分配的内存池

  • 小块:小于内存页(MP_PAGE_SIZE:4096)
  • 大块:大于等于内存页,直接调用malloc分配
    malloc会直接调用mmap分配整页内存(无碎片、管理简单)。

1. 内存地址对齐:

/** * 指针对齐宏:将指针p按alignment字节对齐(转换为size_t计算后转回void*) * 作用:保证分配的内存地址是对齐数的整数倍,提升访问效率 */ #define mp_align_ptr(p, alignment) (void *)((((size_t)p) + (alignment - 1)) & ~(alignment - 1))

2. 内存分配大小对齐:(指针对齐了,内存大小的分配也跟着对齐了,毕竟后面也用不到)

/** * 数值对齐宏:将数值n向上取整到alignment的整数倍 * 原理:(n + 对齐数-1) 抵消余数,再 & ~(对齐数-1) 清零余数位 * 示例:mp_align(24,32) = (24+31) & ~31 = 55 & 0xffffffe0 = 32 */ #define mp_align(n, alignment) (((n) + (alignment - 1)) & ~(alignment - 1))

3. 小块结构体

字段名字段类型核心含义
lastunsigned char *当前内存页中已分配内存的末尾地址(下一次小内存分配的起始位置)
endunsigned char *当前内存页的结束地址(内存页最大可用地址,end - last为剩余可用空间)
nextstruct mp_node_s *链表指针,串联多个小内存页(首个页不足时新建页)
failedsize_t当前小块分配失败次数(没有足够的可用空间提供给外部):失败>4 次时,后续遍历小块分配跳过小块(提升遍历效率)

4. 大块结构体

字段名字段类型核心含义
nextstruct mp_large_s *链表指针,串联所有大内存块管理节点(复用节点,避免频繁 malloc/free)
allocvoid *指向通过malloc分配的大内存块(>4095 字节)的起始地址

大块结构体创建所需内存由小块提供, alloc指向的内存由malloc提供

5. 内存池结构体

字段名字段类型核心含义
maxsize_t小内存块最大阈值(取创建时指定size4095的较小值)
currentstruct mp_node_s *指向当前可分配小内存的页节点(优化分配效率,避免从头遍历)
largestruct mp_large_s *指向大内存块管理链表的头节点
head[0]struct mp_node_s柔性数组,指向首个小内存页节点(内存池创建时初始化的第一个页)

疑问:

问1:指针要对齐?
1. 极致提升 CPU 访问效率(内存池的核心诉求)

内存池的典型场景是「高频分配、高频访问」(比如 Nginx 每秒处理数万请求,每个请求都要分配 / 访问内存),而 CPU 访问内存的效率完全依赖地址对齐:

  • CPU 读取内存不是 “逐字节”,而是按「缓存行 / 字长」批量读取(比如 64 位 CPU 一次读 8 字节,AVX2 指令一次读 32 字节);
  • 若指针地址是 32 字节对齐(MP_ALIGNMENT=32):CPU 一次就能读取完整的 32 字节数据,无需拼接;
  • 若地址不对齐:CPU 需要读 2 次内存,再拼接数据,耗时翻倍(高频场景下,这个损耗会被无限放大)。

举个具体例子(缓存行为32字节的情况下):

  • 地址 = 0x1000(32 对齐):读 32 字节 → 1 次完成;
  • 地址 = 0x1008(非 32 对齐):读 32 字节需要从 0x1008 读到 0x1028 → 跨 2 个 32 字节块,CPU 读 2 次 + 拼接,耗时翻倍。
2. 满足硬件 / 指令的强制要求

内存池常被用于需要和硬件交互的场景(如网络编程、音视频处理):

  • SIMD 指令(如 Intel AVX2、ARM NEON):强制要求内存地址是 32/64 字节对齐,否则指令执行失败;
  • DMA 控制器(网卡 / 磁盘 IO):从内存读取数据时,要求地址是 32 字节对齐,否则拒绝传输;
  • 多核缓存:对齐的地址更容易命中 CPU 缓存(缓存行通常是 32/64 字节),进一步提升性能。
  • 不对齐访问:ARM会崩溃

如果指针地址不对齐,这些硬件 / 指令会直接报错,程序崩溃 —— 这不是 “性能问题”,而是 “可用性问题”。

问2:alignment的取值:
维度8 字节对齐
(malloc默认)
16 字节对齐32 字节对齐64 字节对齐
CPU 访问效率满足 64 位 CPU 基础字长(8 字节),普通访问高效;但跨缓存行概率高匹配部分 CPU 缓存行(16 字节),缓存命中率略高匹配主流 CPU 缓存行(32 字节),缓存命中率最高(数据小于32字节时,无跨行)匹配高端 CPU 缓存行(64 字节),极致缓存效率;但小数据也占整行
内存利用率最高(碎片最少):分配 10 字节仅浪费 6 字节较高:分配 10 字节浪费 6 字节,24 字节浪费 8 字节中等:分配 10/24 字节均浪费 22 字节最低(碎片最多):分配 10/24/32 字节均浪费 54/40/32 字节
硬件兼容性仅满足基础要求(x86 普通程序);不支持 SIMD / 高端 DMA支持部分 ARM 架构 / 简单 DMA;不支持 AVX2支持主流 SIMD(AVX2)、DMA(网卡 / 磁盘)、ARM 高性能场景支持极致硬件(AVX512、高端显卡 / AI 芯片);兼容性最全
代码复杂度无额外复杂度无额外复杂度无额外复杂度无额外复杂度
典型适用场景普通小程序、嵌入式低内存场景一般高性能程序(如普通后台服务)主流高性能组件(Nginx/Redis/ 数据库)极致高性能场景(AI / 音视频编解码 / 高频金融交易)
问3:内存分配的大小必须要对齐吗?

不是必须的,只需满足指针对齐(内存对齐)就可以让大部分系统不崩溃了。这里只是提供接口,代码中实际上未使用。

五、代码实现:

1. 等长分配

// ===============================等长分配=============================== #include <stdio.h> #include <stdlib.h> #include <string.h> #define MEM_PAGE_SIZE 0x1000 // typedef struct mempool_s { int blocksize; int freecount; char *free_ptr; char *mem; } mempool_t; // sdk --> varchar(32); // // 2^n, page_size 4096, block_size: 16, 32, 64, 128 int memp_create(mempool_t *m, int block_size) { if (!m) return -1; m->blocksize = block_size; m->freecount = MEM_PAGE_SIZE / block_size; m->mem = (char *)malloc(MEM_PAGE_SIZE); // if (!m->mem) { // NULL return -2; } memset(m->mem, 0, MEM_PAGE_SIZE); // m->free_ptr = m->mem; int i = 0; char *ptr = m->mem; for (i = 0; i < m->freecount; i++) { *(char **)ptr = ptr + block_size; ptr = ptr + block_size; } *(char **)ptr = NULL; return 0; } void memp_destory(mempool_t *m) { if (!m) return; free(m->mem); } void *memp_alloc(mempool_t *m) { if (!m || m->freecount == 0) return NULL; void *ptr = m->free_ptr; m->free_ptr = *(char **)ptr; m->freecount--; return ptr; } void memp_free(mempool_t *m, void *ptr) { *(char **)ptr = m->free_ptr; m->free_ptr = (char *)ptr; m->freecount++; } // memp_strcpy // memp_memcpy int main() { mempool_t m; memp_create(&m, 32); char *p1 = memp_alloc(&m); printf("memp_alloc : %p\n", p1); sprintf(p1, "hello"); printf("%s\n", p1); memp_free(&m, p1); void *p2 = memp_alloc(&m); printf("memp_alloc : %p\n", p2); void *p3 = memp_alloc(&m); printf("memp_alloc : %p\n", p3); memp_free(&m, p2); void *p4 = memp_alloc(&m); printf("memp_alloc : %p\n", p4); memp_free(&m, p2); }

2. 大小块分配

部分接口介绍:

1. mp_alloc:返回的地址是对齐的,对last指针取整了

2. mp_nalloc: 返回的地址不一定是对齐的

3. mp_alloc_large:使用malloc分配,对齐字节数默认为8

4. mp_memalign:使用posix_memalign分配,对齐字节数可指定

5. mp_reset_pool(内存池重置)

  • large->alloc全部释放并置为NULL,同时将pool->large置为NULL(large节点是由小块分配的,现在小块复位了,相当于large被释放了)
  • node节点(小块)的last全部复位,表示节点还有size空间可用。
  • current节点置为head,同时各node节点的fail全部置为0
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> // =========================大小快分配=============================== #define MP_ALIGNMENT 32 #define MP_PAGE_SIZE 4096 #define MP_MAX_ALLOC_FROM_POOL (MP_PAGE_SIZE-1) #define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1)) #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1)) struct mp_large_s { struct mp_large_s *next; void *alloc; }; struct mp_node_s { unsigned char *last; unsigned char *end; struct mp_node_s *next; size_t failed; }; struct mp_pool_s { size_t max; struct mp_node_s *current; struct mp_large_s *large; struct mp_node_s head[0]; }; struct mp_pool_s *mp_create_pool(size_t size); void mp_destory_pool(struct mp_pool_s *pool); void *mp_alloc(struct mp_pool_s *pool, size_t size); void *mp_nalloc(struct mp_pool_s *pool, size_t size); void *mp_calloc(struct mp_pool_s *pool, size_t size); void mp_free(struct mp_pool_s *pool, void *p); struct mp_pool_s *mp_create_pool(size_t size) { struct mp_pool_s *p; int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s)); if (ret) { return NULL; } p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL; p->current = p->head; p->large = NULL; p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s); p->head->end = p->head->last + size; p->head->failed = 0; return p; } void mp_destory_pool(struct mp_pool_s *pool) { struct mp_node_s *h, *n; struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (l->alloc) { free(l->alloc); } } h = pool->head->next; while (h) { n = h->next; free(h); h = n; } free(pool); } void mp_reset_pool(struct mp_pool_s *pool) { struct mp_node_s *h; struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (l->alloc) { free(l->alloc); } } pool->large = NULL; for (h = pool->head; h; h = h->next) { h->last = (unsigned char *)h + sizeof(struct mp_node_s); h->failed = 0; //新增 } pool->current = pool->head; // 新增 } static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *h = pool->head; size_t psize = (size_t)(h->end - (unsigned char *)h); int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize); if (ret) return NULL; struct mp_node_s *p, *new_node, *current; new_node = (struct mp_node_s*)m; new_node->end = m + psize; new_node->next = NULL; new_node->failed = 0; m += sizeof(struct mp_node_s); m = mp_align_ptr(m, MP_ALIGNMENT); new_node->last = m + size; current = pool->current; for (p = current; p->next; p = p->next) { if (p->failed++ > 4) { // current = p->next; } } p->next = new_node; pool->current = current ? current : new_node; return m; } static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) { void *p = malloc(size); if (p == NULL) return NULL; size_t n = 0; struct mp_large_s *large; for (large = pool->large; large; large = large->next) { if (large->alloc == NULL) { large->alloc = p; return p; } if (n ++ > 3) break; } large = mp_alloc(pool, sizeof(struct mp_large_s)); if (large == NULL) { free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; } void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) { void *p; int ret = posix_memalign(&p, alignment, size); if (ret) { return NULL; } struct mp_large_s *large = mp_alloc(pool, sizeof(struct mp_large_s)); if (large == NULL) { free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; } void *mp_alloc(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *p; if (size <= pool->max) { p = pool->current; do { m = mp_align_ptr(p->last, MP_ALIGNMENT); if ((size_t)(p->end - m) >= size) { p->last = m + size; return m; } p = p->next; } while (p); return mp_alloc_block(pool, size); } return mp_alloc_large(pool, size); // return mp_memalign(pool, size, alignment); 指定 posix_memalign 的分配内存地址对齐的字节数,malloc默认对齐8 } void *mp_nalloc(struct mp_pool_s *pool, size_t size) { unsigned char *m; struct mp_node_s *p; if (size <= pool->max) { p = pool->current; do { m = p->last; if ((size_t)(p->end - m) >= size) { p->last = m+size; return m; } p = p->next; } while (p); return mp_alloc_block(pool, size); } return mp_alloc_large(pool, size); } void *mp_calloc(struct mp_pool_s *pool, size_t size) { void *p = mp_alloc(pool, size); if (p) { memset(p, 0, size); } return p; } // 仅支持大块释放 void mp_free(struct mp_pool_s *pool, void *p) { struct mp_large_s *l; for (l = pool->large; l; l = l->next) { if (p == l->alloc) { free(l->alloc); l->alloc = NULL; return ; } } } int main(int argc, char *argv[]) { int size = 1 << 12; struct mp_pool_s *p = mp_create_pool(size); int i = 0; for (i = 0;i < 10;i ++) { void *mp = mp_alloc(p, 512); // mp_free(mp); } //printf("mp_create_pool: %ld\n", p->max); printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32)); //printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large); int j = 0; for (i = 0;i < 5;i ++) { char *pp = mp_calloc(p, 32); for (j = 0;j < 32;j ++) { if (pp[j]) { printf("calloc wrong\n"); } printf("calloc success\n"); } } //printf("mp_reset_pool\n"); for (i = 0;i < 5;i ++) { void *l = mp_alloc(p, 8192); mp_free(p, l); } mp_reset_pool(p); //printf("mp_destory_pool\n"); for (i = 0;i < 58;i ++) { mp_alloc(p, 256); } mp_destory_pool(p); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 9:22:01

Kubernetes 滚动升级与回滚:生产级实践终极指南

在 Kubernetes 中,发布不是一次 kubectl apply,而是一场风险受控的工程行为。 真正成熟的团队,关注的不只是“如何升级成功”,而是: 升级过程中是否 不中断、不丢请求 新版本异常时能否 秒级止损 整个过程是否 可观测、可审计、可复盘 本文将从 滚动升级原理 → 参数调优…

作者头像 李华
网站建设 2026/4/16 9:26:15

Ollama 安装,流式输出,多模态,思考模型

接入ollama本地模型ollama是大语言模型的运行环境 &#xff0c; 支持将开源的大语言模型以离线的方式部署到本地&#xff0c;进行私有化部署。 这也是企业中常用的方案&#xff0c; 因为本地化部署能保证企业级的数据安全&#xff0c; 降低企业使用成本。可以将我们的 ollama 理…

作者头像 李华
网站建设 2026/4/16 9:22:11

LDAP注入全攻略:从目录遍历到完全控制

LDAP 基础概念 轻量级目录访问协议 (Lightweight Directory Access Protocol, LDAP) 是一种开放的、跨平台的、基于 TCP/IP 的协议&#xff0c;用于访问和维护分布式目录信息服务。目录服务就像一个特殊的数据库&#xff0c;优化用于读取、浏览和搜索&#xff0c;主要存储有关用…

作者头像 李华
网站建设 2026/4/16 10:58:01

广度/深度优先遍历思想

广度优先遍历类似二叉树或者其他算法的层序遍历&#xff0c;一层一层的去搜索。#include <stdio.h> typedef char VertexType typedef int edgetype #define Maxsize 100 typedef struct { VertexType vertex[Maxsize]; //定义一维数组来存储顶点 edgetype arc[Maxsize][…

作者头像 李华
网站建设 2026/4/16 2:36:02

【毕业设计】基于springboot+微信小程序的社区论坛与二手交易平台的设计与实现(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华