C++八股——內存分配

文章目錄

    • 1. 虛擬內存空間
    • 2. malloc和free
    • 3. new和delete
    • 4. 內存池

1. 虛擬內存空間

程序進程的虛擬內存空間是操作系統為每個進程提供的獨立、連續的邏輯地址空間,與物理內存解耦。其核心目的是隔離進程、簡化內存管理,并提供靈活的內存訪問控制。

(圖片取自于:【C++】內存管理分布_c++內存分布-CSDN博客)

自下而上為低地址到高地址。

以Linux x86-64架構為例:

虛擬內存空間分為用戶空間內核空間

  • 用戶空間(低地址 → 高地址):進程可直接訪問的區域,通常占據虛擬地址空間的大部分。
  • 內核空間(高地址保留區):操作系統內核專用,進程無法直接訪問,需通過系統調用交互。

用戶空間分為

  • 代碼段(Text Segment)

    • 地址范圍0x400000 附近(起始地址)。
    • 內容:編譯后的可執行機器指令(如程序的main函數)。
    • 權限:只讀(Read-Only)、可執行(Execute),不可修改。
    • 特點:多個進程可共享同一代碼段(如動態庫)。
  • 初始化數據段(Data Segment)

    • 地址范圍:緊鄰代碼段。
    • 內容:全局變量、靜態變量(已顯式初始化,如 int a = 10;)。
    • 權限:可讀寫(Read-Write),不可執行。
  • 未初始化數據段(BSS Segment)

    • 地址范圍:緊鄰初始化數據段。
    • 內容:未顯式初始化的全局/靜態變量(如 int b;),默認值為0。
    • 權限:可讀寫,不可執行。
  • 堆(Heap)

    • 地址范圍:向高地址動態增長。
    • 內容:動態分配的內存(如 malloc()new)。
    • 權限:可讀寫,不可執行。
    • 管理:由程序員控制分配/釋放,可能產生內存碎片。
  • 內存映射區域(Memory Mapping Segment)

    • 地址范圍:堆與棧之間的動態區域。

    • 內容

      • 動態鏈接庫(如libc.so)。
      • 文件映射(mmap系統調用,如加載共享內存或大文件)。
      • 匿名映射(用于大塊內存分配,如某些malloc實現)。
    • 權限:可自定義(讀/寫/執行)。

  • 棧(Stack)

    • 地址范圍:用戶空間頂端附近,向低地址增長。

    • 內容

      • 函數調用棧幀(局部變量、函數參數、返回地址)。
      • 線程棧(每個線程有獨立棧空間)。
    • 權限:可讀寫,不可執行。

    • 管理:自動分配/釋放,由編譯器控制,大小有限(可能棧溢出)。

  • 環境變量與命令行參數

    • 地址范圍:棧的頂部區域。
    • 內容argv(命令行參數)、envp(環境變量)的字符串數組。
    • 權限:可讀寫。

內核空間

  • 地址范圍:64位系統中通常為高地址的 0xffff800000000000 以上。
  • 內容
    • 內核代碼、數據結構。
    • 進程頁表、硬件驅動、中斷處理程序等。
  • 權限:僅內核態可訪問,用戶態訪問會觸發段錯誤。

2. malloc和free

參考文章:malloc 底層實現及原理_malloc底層原理-CSDN博客

malloc

  • 當分配的大小小于128KB時,通過brk()系統調用從堆段分配內存
  • 當分配的大小大于等于128KB時,通過mmap()系統調用從文件映射段分配內存

注意:這兩種方式分配的都是虛擬內存,沒有分配物理內存。在第一次訪問已分配的虛擬地址空間的時候,發生缺頁中斷,操作系統負責分配物理內存,然后建立虛擬內存和物理內存之間的映射關系。


free

在實際分配內存時會多分配16字節(位于元數據之前)用來記錄內存塊描述信息,其中包括內存塊的大小。

當調用free釋放內存時,傳入的指針會首先向前偏移16字節,獲取內存塊的信息,然后再釋放。

  • 對于brk()申請的小空間,會標記為空間,并不會直接釋放。當連續空閑空間大于128KB時會執行內存緊縮操作(trim)
  • 對于mmap()申請的大空間,會調用munmap()歸還給操作系統

3. new和delete

參考文章:【C++】內存管理分布_c++內存分布-CSDN博客

new 的底層步驟

  • 內存分配:調用 operator new 函數(內部通常基于 malloc)申請指定大小的內存。
  • 對象構造:在分配的內存上調用對象的構造函數(初始化成員變量等)。
  • 異常處理:若內存不足,operator new 拋出 std::bad_alloc 異常(除非使用 nothrow 版本)。

底層展開:

void* ptr = operator new(sizeof(MyClass)); // 內部調用 malloc
MyClass::MyClass(ptr);                     // 構造函數

delete 的底層步驟

  • 對象析構:調用對象的析構函數(清理資源,如釋放句柄)。
  • 內存釋放:調用 operator delete 函數(內部通常基于 free)釋放內存。

底層展開:

MyClass::~MyClass(obj);      // 析構函數
operator delete(obj);        // 內部調用 free

mallocfree的對比

特性new/deletemalloc/free
語言層面C++ 運算符,支持重載C 標準庫函數,不可重載
內存初始化調用構造函數/析構函數僅分配/釋放原始內存,無初始化邏輯
類型安全返回類型明確指針(如 MyClass*返回 void*,需手動類型轉換
內存大小計算自動計算類型所需大小(如 new int需手動指定字節數(如 malloc(4)
錯誤處理內存不足時拋出異常(可捕獲)返回 NULL,需檢查返回值
數組支持支持 new[]delete[]需手動計算數組大小,無內置支持
底層擴展性可自定義 operator new/operator delete無法修改 malloc/free 行為
適用場景C++ 對象管理(含構造/析構)原始內存操作或與 C 代碼交互

異常與錯誤處理

  • new:失敗時拋出 std::bad_alloc,可通過 try-catch 捕獲。

    try {int* arr = new int[1000000000000];
    } catch (const std::bad_alloc& e) {std::cerr << "內存不足: " << e.what() << std::endl;
    }
    
  • malloc:失敗時返回 NULL,需顯式檢查。

    int* arr = (int*)malloc(1000000000000 * sizeof(int));
    if (arr == nullptr) {perror("malloc 失敗");
    }
    

內存對齊與重載

  • new:支持自定義內存對齊(C++17 的 align_val_t)和重載。

    // 自定義 operator new
    void* operator new(size_t size, const char* tag) {std::cout << "通過標簽分配: " << tag << std::endl;return malloc(size);
    }
    MyClass* obj = new ("DEBUG") MyClass();
    
  • malloc:對齊由實現決定,無法直接定制。

數組處理

  • new[]:自動計算數組元素總大小,并為每個元素調用構造函數。

    MyClass* arr = new MyClass[5]; // 調用 5 次構造函數
    delete[] arr;                   // 調用 5 次析構函數
    
  • malloc:需手動計算總字節數,且不構造對象。

    MyClass* arr = (MyClass*)malloc(5 * sizeof(MyClass));
    free(arr); // 不會調用析構函數,可能導致資源泄漏
    

4. 內存池

內存池(Memory Pool)是一種預先分配并統一管理內存資源的技術,旨在優化動態內存分配的效率和性能。

4.1 基本概念

  • 預先分配:在程序初始化階段,一次性向操作系統申請一大塊連續內存(稱為“池”)。
  • 自主管理:程序自行管理池內的內存分配與回收,避免頻繁調用系統級函數(如malloc/free)。
  • 按需分配:從池中劃分小塊內存供程序使用,釋放時標記為可復用而非立即歸還操作系統。

4.2 核心優勢

特性傳統malloc/free內存池
分配速度需遍歷復雜數據結構(如Bins)直接定位空閑塊,速度極快
內存碎片易產生外部/內部碎片碎片可控,甚至完全消除(固定塊)
系統調用開銷頻繁調用brk/mmap僅初始化和銷毀時調用
線程安全全局鎖可能引發競爭可設計為線程私有池或無鎖結構
適用場景通用、動態需求高頻次、固定/可預測內存需求

4.3 實現方式

固定大小內存池

  • 機制:池中所有內存塊大小相同(如4KB)。

  • 管理:使用空閑鏈表(Free List)跟蹤可用塊。

    struct MemoryBlock {MemoryBlock* next; // 指向下一個空閑塊
    };
    MemoryBlock* free_list; // 空閑鏈表頭
    
  • 操作

    • 分配:從鏈表頭部取一個塊,時間復雜度O(1)。
    • 釋放:將塊插回鏈表頭部,時間復雜度O(1)。
  • 優點:零碎片、極快分配。

  • 缺點:無法處理變長需求,可能浪費內存。

可變大小內存池

  • 機制:支持不同大小的內存請求。
  • 管理
    • 分離空閑鏈表:為不同大小范圍維護多個鏈表(如8B、16B、32B…)。
    • 伙伴系統(Buddy System):按2的冪次分割內存塊,合并相鄰空閑塊。
      • 分配時向上取整到最近的2^n大小。
      • 釋放時檢查“伙伴塊”是否空閑,若空閑則合并。
  • 優點:靈活支持變長請求,減少碎片。
  • 缺點:管理復雜度高,存在內部碎片。

4.4 典型應用場景

  • 高頻次小對象分配

    • 如網絡服務器為每個請求分配臨時緩沖區。
    • 示例:Nginx使用內存池管理HTTP請求資源。
  • 實時系統

    確保內存分配時間確定性,避免傳統malloc的不可預測延遲。

  • 游戲開發

    快速創建/銷毀大量游戲實體(如子彈、粒子效果),通過對象池(Object Pool)實現。

  • 嵌入式系統

    資源受限環境,需嚴格控制內存使用和碎片。

4.5 示例

#define BLOCK_SIZE 64     // 固定塊大小
#define POOL_SIZE 100     // 池中塊數量typedef struct MemoryBlock {struct MemoryBlock* next;
} MemoryBlock;MemoryBlock* free_list = NULL;// 初始化內存池
void init_pool() {static char pool[BLOCK_SIZE * POOL_SIZE];for (int i = 0; i < POOL_SIZE; i++) {MemoryBlock* block = (MemoryBlock*)(pool + i * BLOCK_SIZE);block->next = free_list;free_list = block;}
}// 分配內存
void* pool_alloc() {if (!free_list) return NULL; // 池耗盡MemoryBlock* block = free_list;free_list = free_list->next;return (void*)block;
}// 釋放內存
void pool_free(void* ptr) {MemoryBlock* block = (MemoryBlock*)ptr;block->next = free_list;free_list = block;
}

(注:以上內容參考自DeepSeek)

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

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

相關文章

【Linux基礎】網絡相關命令

目錄 netstat命令 1.1 命令介紹 1.2 命令格式 1.3 常用選項 1.4 常用命令實例 1.4.1 顯示所有TCP連接 1.4.2 查看路由表 1.4.3 實時監控網絡接口流量 1.4.4 查看監聽中的端口以及關聯進程 ping命令 2.1 命令介紹 2.2 命令格式 2.3 常用選項 2.4 常用示例 ifconfi…

adb 實用命令匯總

版權歸作者所有&#xff0c;如有轉發&#xff0c;請注明文章出處&#xff1a;https://cyrus-studio.github.io/blog/ 基礎adb命令 # 重啟adb adb kill-server# 查看已連接的設備 adb devices# 進入命令行 adb shell# 使用 -s 參數來指定設備 adb -s <設備序列號> shell…

C#管道通訊及傳輸信息丟失的原因

以下是C#管道通訊客戶端/服務端共用類 namespace PipeCommunication { /// <summary> /// 管道信息回調通知 /// </summary> /// <param name"msg"></param> public delegate void PipeMessageEventHandler(string msg…

MixTeX - 支持CPU推理的多模態LaTeX OCR

文章目錄 一、項目概覽相關資源核心特性技術特點 二、安裝三、使用說明環境要求 四、版本更新五、當前限制 一、項目概覽 MixTeX是一款創新的多模態LaTeX識別小程序&#xff0c;支持本地離線環境下的高效CPU推理。 無論是LaTeX公式、表格還是混合文本&#xff0c;MixTeX都能輕…

簡單 Linux 字符設備驅動程序

注&#xff1a;本文為 “Linux 字符設備驅動” 相關文章合輯。 英文引文&#xff0c;機翻未校。 中文引文&#xff0c;略作重排。 未整理去重&#xff0c;如有內容異常&#xff0c;請看原文。 Simple Linux character device driver 簡單 Linux 字符設備驅動程序 Oleg Kutko…

NX949NX952美光科技閃存NX961NX964

NX949NX952美光科技閃存NX961NX964 在半導體存儲領域&#xff0c;美光科技始終扮演著技術引領者的角色。其NX系列閃存產品線憑借卓越的性能與創新設計&#xff0c;成為數據中心、人工智能、高端消費電子等場景的核心組件。本文將圍繞NX949、NX952、NX961及NX964四款代表性產品…

協議路由與路由協議

協議路由”和“路由協議”聽起來相似&#xff0c;但其實是兩個完全不同的網絡概念。下面我來分別解釋&#xff1a; 一、協議路由&#xff08;Policy-Based Routing&#xff0c;PBR&#xff09; ? 定義&#xff1a; 協議路由是指 根據預設策略&#xff08;策略路由&#xff0…

Linux510 ssh服務 ssh連接

arning: Permanently added ‘11.1.1.100’ (ECDSA) to the list of known hosts. rooot11.1.1.100’s password: Permission denied, please try again. rooot11.1.1.100’s password: Permission denied, please try again 還沒生效 登不上了 失效了 sshcaozx26成功登錄 …

金融學知識筆記

金融學知識筆記 一、引言 金融學它結合了數學、概率論、統計學、經濟學和計算機科學等多學科的知識&#xff0c;用于解決金融領域中的各種問題&#xff0c;如金融衍生品定價、投資組合優化、風險管理和固定收益證券分析等。通過對金融學的學習&#xff0c;我們可以更好地理解…

AB測試面試題

AB測試面試題 常考AB測試問答題(1)AB測試的優缺點是什么?(2)AB測試的一般流程/介紹一下日常工作中你是如何做A/B實驗的?(3)第一類錯誤 vs 第二類錯誤 vs 你怎么理解AB測試中的第一、二類錯誤?(4)統計顯著=實際顯著?(5)AB測試效果統計上不顯著?(6)實驗組優于對…

USR-M100采集數據并提交MQTT服務器

本文為記錄備忘&#xff0c;不做過多解釋。 模塊自身帶有2路數字量輸入&#xff0c;2路模擬量輸入&#xff0c;2路485接口 數字量接報警輸入&#xff0c;模擬量接壓力傳感器&#xff0c;液位傳感器&#xff0c;485接口分別接流量計&#xff0c;溫濕度傳感器。 正確接線&…

Octave 繪圖快速入門指南

目錄 1. 基本的 2D 繪圖 2. 自定義圖形樣式 3. 繪制散點圖 4. 繪制柱狀圖 5. 繪制直方圖 6. 3D 繪圖 6.6.1 3D 曲面圖 6.6.2 3D 散點圖 7. 繪制極坐標 8. 多子圖繪制 總結 Octave 是一個類似于 MATLAB 的開源數學軟件&#xff0c;廣泛用于數值計算和數據分析。它提供…

RabbitMQ--基礎篇

RabbitMQ 簡介&#xff1a;RabbitMQ 是一種開源的消息隊列中間件&#xff0c;你可以把它想象成一個高效的“郵局”。它專門負責在不同應用程序之間傳遞消息&#xff0c;讓系統各部分能松耦合地協作 優勢&#xff1a; 異步處理&#xff1a;比如用戶注冊后&#xff0c;主程序將發…

【MySQL】事務(重點)

目錄 一、什么是事務&#xff1a; 二、事務的前置知識了解 引擎是否支持事務 事務的提交方式 事務操作的前置準備&#xff1a; 三、事務回滾&#xff1a; 四、事務崩潰&#xff1a; 原子性&#xff1a; 持久性&#xff1a; 五、自動提交和手動提交&#xff1a; 六、…

C++STL——stack,queue

stack與queue 前言容器適配器deque 前言 本篇主要講解stack與queue的底層&#xff0c;但并不會進行實現&#xff0c;stack的接口 queue的接口 &#xff0c;關于stack與queue的接口在這里不做講解&#xff0c;因為通過前面的對STL的學習&#xff0c;這些接口都是大同小異的。 …

STM32智能手表:基于FreeRTOS

引言 隨著物聯網和可穿戴設備的快速發展&#xff0c;智能手表作為典型代表&#xff0c;集成了傳感器數據采集、實時顯示、無線通信等多項功能。本文將深入剖析一個基于STM32和FreeRTOS的智能手表項目&#xff0c;從硬件架構到軟件設計&#xff0c;逐步講解如何構建一個完整的嵌…

leetcode504.七進制數

標簽&#xff1a;進制轉換 機試真題 給定一個整數 num&#xff0c;將其轉化為 7 進制&#xff0c;并以字符串形式輸出。 示例 1: 輸入: num 100 輸出: "202" 示例 2: 輸入: num -7 輸出: "-10" 思路&#xff1a;求n進制就是循環取余數&#xff0c;…

中國古代史2

夏朝&#xff08;公元前2070-公元前1600年&#xff09; 1.禹建立了我國歷史上第一個奴隸制國家–夏朝&#xff0c;定都陽城。禹傳啟&#xff0c;世襲制代替禪讓制。 2.夏代都城&#xff1a;二里頭遺址位于今河南洛陽偃師二里頭村。發現了大型綠松石龍形器&#xff0c;被命名為…

死鎖的形成

死鎖的形成 背景學習資源死鎖的本質 背景 面試可能會被問到. 學習資源 一個案例: https://www.bilibili.com/video/BV1pz421Y7kM 死鎖的本質 互相持有對方的資源. 存在資源競爭都沒有釋放. 可能出現死鎖. insert into demo_user (no, name) values (6, ‘test1’) on dupl…

MapReduce架構-打包運行

&#xff08;一&#xff09;maven打包 MapReduce是一個分布式運算程序的編程框架&#xff0c;是用戶開發“基于Hadoop的數據分析應用”的核心框架。 MapReduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分布式運算程序&#xff08;例如&#xff1a;jar…