[C語言實戰]C語言內存管理實戰:實現自定義malloc與free(四)

[C語言實戰]C語言內存管理實戰:實現自定義malloc與free(四)

摘要:通過實現簡化版的內存管理器,深入理解動態內存分配的核心原理。本文包含內存塊設計、分配算法、空閑合并策略的完整實現,并附可運行的代碼和測試用例。

一、動態內存管理原理剖析

1.1 內存分配的核心需求

  • 動態分配:程序運行時按需請求內存
  • 碎片管理:減少內部碎片(分配過大)和外部碎片(空閑塊分散)
  • 效率平衡:時間(搜索速度)與空間(利用率)的權衡

1.2 內存塊結構設計

typedef struct mem_block {size_t size;         // 可用內存大小(不含頭部)int free;            // 空閑標記(1=空閑,0=占用)struct mem_block *next;  // 鏈表指針
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block)  // 頭部元數據大小

內存布局

[ Header | Allocated Memory ] -> [ Header | Allocated Memory ] -> ...

1.3 分配策略對比

策略優點缺點
首次適應搜索速度快容易產生外部碎片
最佳適應內存利用率高搜索成本高
最差適應減少外部碎片大塊請求可能失敗

二、自定義malloc/free實現代碼(test_alloc.c)

2.1 內存池初始化

// 內存塊結構體定義(必須在使用前聲明)
typedef struct mem_block {size_t size;          // 可用內存大小(不含頭部)int free;             // 空閑標記(1=空閑,0=占用)struct mem_block *next; // 鏈表指針
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block) // 頭部元數據大小
#define HEAP_SIZE (1024*1024)  // 1MB堆空間static char memory_pool[HEAP_SIZE];
static mem_block *free_list = NULL;void init_memory_pool() {free_list = (mem_block*)memory_pool;free_list->size = HEAP_SIZE - BLOCK_HEADER_SIZE;free_list->free = 1;free_list->next = NULL;
}

2.2 malloc實現(首次適應算法)

void* my_malloc(size_t size) {if (!free_list) init_memory_pool();  // 首次調用初始化mem_block *curr = free_list;while (curr) {if (curr->free && curr->size >= size) {// 切割內存塊(剩余空間至少能存放頭部)if (curr->size > size + BLOCK_HEADER_SIZE) {mem_block *new_block = (mem_block*)((char*)curr + BLOCK_HEADER_SIZE + size);new_block->size = curr->size - size - BLOCK_HEADER_SIZE;new_block->free = 1;new_block->next = curr->next;curr->size = size;curr->next = new_block;}curr->free = 0;return (void*)((char*)curr + BLOCK_HEADER_SIZE);  // 返回用戶空間指針}curr = curr->next;}return NULL;  // 內存不足
}

2.3 free實現(相鄰塊合并)

void my_free(void *ptr) {if (!ptr) return;mem_block *block = (mem_block*)((char*)ptr - BLOCK_HEADER_SIZE);block->free = 1;// 前向合并mem_block *curr = free_list;while (curr) {if ((char*)curr + BLOCK_HEADER_SIZE + curr->size == (char*)block) {curr->size += BLOCK_HEADER_SIZE + block->size;curr->next = block->next;block = curr;}curr = curr->next;}// 后向合并if (block->next && block->next->free) {block->size += BLOCK_HEADER_SIZE + block->next->size;block->next = block->next->next;}
}

2.4 test_alloc.c完整代碼

#include <stddef.h>   // 解決NULL和size_t未定義問題
#include <stdio.h>    // 用于printf調試輸出
#include <assert.h>   // 用于斷言測試// 內存塊結構體定義(必須在使用前聲明)
typedef struct mem_block {size_t size;          // 可用內存大小(不含頭部)int free;             // 空閑標記(1=空閑,0=占用)struct mem_block *next; // 鏈表指針
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block) // 頭部元數據大小
#define HEAP_SIZE (1024*1024)  // 1MB堆空間static char memory_pool[HEAP_SIZE];
static mem_block *free_list = NULL;void init_memory_pool() {free_list = (mem_block*)memory_pool;free_list->size = HEAP_SIZE - BLOCK_HEADER_SIZE;free_list->free = 1;free_list->next = NULL;
}void* my_malloc(size_t size) {if (!free_list) init_memory_pool();  // 首次調用初始化mem_block *curr = free_list;while (curr) {if (curr->free && curr->size >= size) {// 切割內存塊(剩余空間至少能存放頭部)if (curr->size > size + BLOCK_HEADER_SIZE) {mem_block *new_block = (mem_block*)((char*)curr + BLOCK_HEADER_SIZE + size);new_block->size = curr->size - size - BLOCK_HEADER_SIZE;new_block->free = 1;new_block->next = curr->next;curr->size = size;curr->next = new_block;}curr->free = 0;return (void*)((char*)curr + BLOCK_HEADER_SIZE);  // 返回用戶空間指針}curr = curr->next;}return NULL;  // 內存不足
}void my_free(void *ptr) {if (!ptr) return;mem_block *block = (mem_block*)((char*)ptr - BLOCK_HEADER_SIZE);block->free = 1;// 前向合并mem_block *curr = free_list;while (curr) {if ((char*)curr + BLOCK_HEADER_SIZE + curr->size == (char*)block) {curr->size += BLOCK_HEADER_SIZE + block->size;curr->next = block->next;block = curr;}curr = curr->next;}// 后向合并if (block->next && block->next->free) {block->size += BLOCK_HEADER_SIZE + block->next->size;block->next = block->next->next;}
}// 內存狀態打印函數
void print_memory_map() {mem_block *curr = free_list;printf("Memory Map:\n");while (curr) {printf("[%s | Size:%6zu] -> ", curr->free ? "FREE " : "USED ", curr->size);curr = curr->next;}printf("NULL\n");
}/****************** 測試代碼 ******************/
int main() {// 基礎功能測試printf("=== 基礎分配測試 ===\n");int *arr = (int*)my_malloc(10*sizeof(int));assert(arr != NULL);printf("分配成功,地址:%p\n", arr);print_memory_map();my_free(arr);printf("釋放后內存狀態:\n");print_memory_map();return 0;
}

三、驗證步驟

3.1 測試環境搭建

# 編譯測試程序(保存為 test_alloc.c)
gcc -o test_alloc test_alloc.c -Wall -Wextra
# 運行測試程序
./test_alloc

3.2 預期結果

在這里插入圖片描述

四、標準庫malloc/free實現原理對比

4.1 glibc的ptmalloc核心設計

/* glibc的malloc_chunk結構(簡化版)*/
struct malloc_chunk {size_t      prev_size;  /* 前塊大小(當空閑時有效)*/size_t      size;       /* 塊大小及標志位 */struct malloc_chunk* fd; /* 空閑鏈表的向前指針 */struct malloc_chunk* bk; /* 空閑鏈表的向后指針 */
};
核心機制對比表
特性自定義實現glibc ptmalloc
內存來源靜態數組brk()/mmap()系統調用
分配粒度按需切割16字節對齊
空閑管理單向鏈表bins數組(fastbin/smallbin等)
線程安全使用arena鎖
大內存處理不支持使用mmap獨立映射
碎片處理相鄰合并top chunk回收機制

4.2 標準庫內存分配流程

malloc調用
請求大小<=128KB?
在arena中查找fastbin/smallbin
使用mmap直接分配
找到合適塊?
拆分并返回內存
擴展top chunk
調用brk調整堆頂
創建獨立內存映射

五、標準庫高級特性實現

5.1 內存對齊分配

// glibc的memalign實現原理
void* glibc_memalign(size_t alignment, size_t size) {// 1. 分配額外空間用于對齊調整void* raw_ptr = malloc(size + alignment - 1);// 2. 計算對齊地址uintptr_t addr = (uintptr_t)raw_ptr;uintptr_t aligned_addr = (addr + alignment - 1) & ~(alignment - 1);// 3. 存儲原始指針用于free((void**)aligned_addr)[-1] = raw_ptr;return (void*)aligned_addr;
}

5.2 內存池優化技術

/* glibc的tcache(線程緩存)結構 */
struct tcache_entry {struct tcache_entry *next;
};struct tcache_perthread_struct {char counts[TCACHE_MAX_BINS];  // 每個bin的計數struct tcache_entry *entries[TCACHE_MAX_BINS];
};
tcache工作流程:
  1. 每個線程維護獨立緩存
  2. 小對象優先從tcache分配
  3. 釋放時先返回tcache
  4. tcache滿時退回全局arena

六、混合使用建議

6.1 何時使用自定義實現

實時系統
已知分配模式
理解原理
需要確定性行為
自定義
內存受限環境
教學/調試

6.2 標準庫最佳實踐

// 示例:使用malloc_trim主動歸還內存
#include <malloc.h>void critical_memory_task() {// 執行前清理內存malloc_trim(0);// 執行關鍵任務// ...// 任務完成后再次清理malloc_trim(0);
}

七、擴展實驗建議

7.1 Hook標準庫函數

// 使用dlsym攔截malloc調用
#include <dlfcn.h>static void* (*real_malloc)(size_t) = NULL;void* malloc(size_t size) {if (!real_malloc) real_malloc = dlsym(RTLD_NEXT, "malloc");printf("申請 %zu 字節內存\n", size);return real_malloc(size);
}

編譯命令:gcc -shared -ldl -fPIC -o libmymalloc.so myhook.c

7.2 內存泄漏檢測

#define _GNU_SOURCE
#include <dlfcn.h>struct alloc_record {void* ptr;size_t size;const char* file;int line;
};static struct alloc_record allocs[MAX_RECORDS];void* dbg_malloc(size_t s, const char* file, int line) {void *p = malloc(s);add_record(p, s, file, line);return p;
}void dbg_free(void *p) {remove_record(p);free(p);
}// 使用宏覆蓋標準函數
#define malloc(s) dbg_malloc(s, __FILE__, __LINE__)
#define free(p) dbg_free(p)

八、標準庫實現演進

8.1 歷史版本對比

版本特性改進點
glibc 2.3引入ptmalloc2多arena支持
glibc 2.10增加tcache線程局部緩存提升性能
glibc 2.26移除malloc hooks增強安全性
glibc 2.32新增malloc_info導出XML格式信息方便內存分析工具集成

8.2 第三方實現對比

// jemalloc的分配器注冊(示例)
#include <jemalloc/jemalloc.h>int main() {// 顯式使用jemallocvoid *p = je_malloc(1024);je_free(p);
}

8.3 主流分配器對比

特性ptmallocjemalloctcmalloc
設計目標通用型低碎片多線程優化
線程緩存tcache自動管理thread cache
大內存處理mmapextent中央堆
適用場景通用服務器內存敏感型應用高并發Web服務

希望本教程對您有幫助,請點贊??收藏?關注支持!歡迎在評論區留言交流技術細節!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84444.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84444.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84444.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

YOLOv8源碼修改(5)- YOLO知識蒸餾(下)設置蒸餾超參數:以yolov8-pose為例

目錄 前言 1. 不同蒸餾算法資源占用 2. 不動態調整蒸餾損失 2.1 訓練定量化結果 2.1 訓練結果可視化結果 3. 動態調整蒸餾損失權重及實驗分析 3.1 余弦衰減和指數衰減 3.2 CWD蒸餾損失 3.3 MGD蒸餾損失 3.4 AT蒸餾損失 3.5 SKD和PKD蒸餾損失 4. 調權重心得總結 5…

歷年華東師范大學保研上機真題

2025華東師范大學保研上機真題 2024華東師范大學保研上機真題 2023華東師范大學保研上機真題 在線測評鏈接&#xff1a;https://pgcode.cn/school?classification1 簡單一位數代數式計算 題目描述 給一個小學生都會算的1位數與1位數運算的代數式&#xff0c;請你求出這個表…

Oracle 中 SHRINK 與 MOVE 操作的比較

Oracle 中 SHRINK 與 MOVE 操作的比較 在 Oracle 數據庫中&#xff0c;SHRINK 和 MOVE 都是用于重組表和索引以減少空間碎片的重要操作&#xff0c;但它們在實現方式和適用場景上有顯著區別。 SHRINK 操作 基本語法 ALTER TABLE table_name SHRINK SPACE [COMPACT] [CASCAD…

展銳 Android 15 鎖定某個App版本的實現

Android 15 系統鎖定Antutu版本的實現方法 在Android系統開發中,有時需要鎖定特定應用的版本以確保系統穩定性或測試一致性。本文將介紹如何通過修改Android源碼來鎖定Antutu跑分軟件的版本。 修改概述 這次修改主要涉及以下幾個方面: 禁用產品復制文件的檢查添加指定版本…

視頻剪輯SDK定制開發技術方案與報價書優雅草卓伊凡

視頻剪輯SDK定制開發技術方案與報價書-優雅草卓伊凡 一、項目概述 客戶需求&#xff1a;開發一套跨平臺&#xff08;Android/iOS/Uni-App&#xff09;視頻剪輯SDK&#xff0c;包含AI字幕提取、轉場特效、文字疊加、背景音樂、濾鏡、背景替換、動態貼紙等功能。 報價范圍&#…

BGP為什么要配置對等IP?

本文由deepseek生成&#xff0c;特此聲明 一、為什么要配置對等體IP&#xff1f; 1. 明確標識鄰居身份 路由協議需求&#xff1a;動態路由協議&#xff08;如BGP、OSPF、RIP&#xff09;需要路由器之間建立鄰居關系以交換路由信息。配置對等體IP是為了唯一標識鄰居路由器&…

Qt中配置文件讀寫

1. 保存分組數據到配置文件 #include <QSettings>void saveNetworkConfig() {QSettings settings("network.ini", QSettings::IniFormat);// 網絡配置分組settings.beginGroup("Network");// 源地址配置settings.beginGroup("Source");se…

Linux 的編輯器--vim

1.Linux編輯器-vim使? vi/vim的區別簡單點來說&#xff0c;它們都是多模式編輯器&#xff0c;不同的是vim是vi的升級版本&#xff0c;它不僅兼容vi的所有指令&#xff0c;?且還有?些新的特性在??。例如語法加亮&#xff0c;可視化操作不僅可以在終端運?&#xff0c;也可以…

SAP Commerce(Hybris)開發實戰(二):登陸生成token問題

問題簡述 最近處理Hybris框架標準的登陸功能&#xff0c;遇到一個問題&#xff1a;用兩個不同的瀏覽器&#xff0c;同時登陸一個賬號&#xff0c;會同時生成兩個不同的token和refreshToken。 問題原因 解決了其實非常簡單&#xff0c;就是Hybris的Employee表中&#xff0c;有一…

c/c++的opencv椒鹽噪聲

在 C/C 中實現椒鹽噪聲 椒鹽噪聲&#xff08;Salt-and-Pepper Noise&#xff09;&#xff0c;也稱為脈沖噪聲&#xff08;Impulse Noise&#xff09;&#xff0c;是數字圖像中常見的一種噪聲類型。它的特點是在圖像中隨機出現純白色&#xff08;鹽&#xff09;或純黑色&#x…

LIEDNet: A Lightweight Network for Low-light Enhancement and Deblurring論文閱讀

摘要 夜間拍攝的圖像常常面臨諸如低光和模糊等挑戰&#xff0c;這些問題主要是由于昏暗環境和長時間曝光的頻繁使用所導致。現有方法要么獨立處理這兩種退化問題&#xff0c;要么依賴于通過復雜機制生成的精心設計的先驗知識&#xff0c;這導致了較差的泛化能力和較高的模型復…

談談worldquant中設置的幾個意思

Decay 是一個設置&#xff0c;用于確定要反映多少過去的位置。正如我們之前詳細介紹的那樣&#xff0c;Decay 值越高&#xff0c;Alpha 周轉率越低。但是&#xff0c;請注意&#xff0c;Alpha 的夏普比率可能會隨著信息延遲而降低。 創建 Alpha 時&#xff0c;頭寸可能會集中在…

大模型和AI工具匯總(一)

一、國內可免費使用的大模型&#xff08;持續更新&#xff09; DeepSeek 模型介紹&#xff1a;DeepSeek 系列包括 DeepSeek V3&#xff08;通用場景&#xff09;、DeepSeek R1&#xff08;推理模型&#xff09;&#xff0c;支持高達 64K 上下文長度&#xff0c;中文場景表現優…

HarmonyOS NEXT 技術特性:分布式軟總線技術架構

HarmonyOS NEXT 技術特性&#xff1a;分布式軟總線技術架構 隨著物聯網發展&#xff0c;2030 預計全球聯網設備達 2000 億&#xff0c;異構設備互聯難題凸顯&#xff0c;分布式軟總線作為 HarmonyOS 生態核心&#xff0c;以軟件虛擬總線打破物理局限&#xff0c;讓跨品牌設備即…

什么是VR展館?VR展館的實用價值有哪些?

VR展館&#xff0c;重塑展覽體驗。在數字化時代浪潮的推動下&#xff0c;傳統的實體展館經歷前所未有的變革。作為變革的先鋒&#xff0c;VR展館以無限的潛力&#xff0c;成為展覽行業的新寵。 VR展館&#xff0c;即虛擬現實展館&#xff0c;是基于VR&#xff08;Virtual Real…

VLA模型:自動駕駛與機器人行業的革命性躍遷,端到端智能如何重塑未來?

當AI開始操控方向盤和機械臂&#xff0c;人類正在見證一場靜默的產業革命。 2023年7月&#xff0c;谷歌DeepMind拋出一枚技術核彈——全球首個視覺語言動作模型&#xff08;VLA&#xff09;RT-2橫空出世。這個能將“把咖啡遞給穿紅衣服的阿姨”這類自然語言指令直接轉化為機器人…

華為OD機試真題——出租車計費/靠譜的車 (2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳實現

2025 A卷 100分 題型 本專欄內全部題目均提供Java、python、JavaScript、C、C++、GO六種語言的最佳實現方式; 并且每種語言均涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、3個測試用例以及綜合分析; 本文收錄于專欄:《2025華為OD真題目錄+全流程解析+備考攻略+經驗分…

40 歲 Windows 開啟 AI 轉型:從系統到生態的智能重構

在科技快速發展的當下&#xff0c;人工智能成為驅動各領域變革的核心力量&#xff0c;擁有 40 年歷史的 Windows 也開啟了向 AI 的全面轉型。2025 年 5 月 19-22 日西雅圖 Build 2025 開發者大會上&#xff0c;微軟展示了 Windows 11 向 AI 智能體核心平臺轉型的戰略&#xff0…

Python實例題:Python3實現可控制肉雞的反向Shell

目錄 Python實例題 題目 代碼實現 reverse_shell_client.py reverse_shell_server.py 實現原理 反向連接機制&#xff1a; 命令執行與傳輸&#xff1a; 功能特點&#xff1a; 關鍵代碼解析 服務端命令處理 客戶端命令執行 客戶端持久化連接 使用說明 啟動服務端…

AWS EC2 使用Splunk DB connect 連接 RDS mysql

1: 先創建 RDS mysql: 我們選擇free: 選擇free 過后,自動生成single instance, 沒有垮AZ 的db 設置。 選擇密碼登入: 注意:上面設置密碼的時候,特別提示:不能有特殊字符,我就設置了: mypassword 下面可以選擇通過EC2 連接,當然也可以不選: