第十三章:優化內存管理_《C++性能優化指南》_notes

優化內存管理

      • 一、內存管理基礎概念
      • 二、自定義分配器
      • 三、智能指針優化
        • 重點知識
        • 代碼示例:智能指針性能對比
      • 四、性能優化關鍵點總結
      • 多選題
      • 設計題
      • 答案與詳解
        • 多選題答案
        • 設計題示例答案(第1題)


一、內存管理基礎概念

重點知識

  1. 動態內存分配開銷
    • newdelete涉及系統調用,頻繁操作會導致性能瓶頸
    • 內存碎片化會降低內存利用率
  2. 自定義內存管理
    • 預分配內存塊(內存池)
    • 類專屬內存管理器
    • 自定義分配器

代碼示例:類專屬內存管理器

#include <iostream>
#include <vector>class MemoryPool {
public:static void* Allocate(size_t size) {if (!freeList.empty()) {void* ptr = freeList.back();freeList.pop_back();return ptr;} else {return ::operator new(size); // 系統默認分配}}static void Deallocate(void* ptr, size_t size) {freeList.push_back(ptr);}private:static std::vector<void*> freeList;
};std::vector<void*> MemoryPool::freeList;class MyObject {
public:void* operator new(size_t size) {return MemoryPool::Allocate(size);}void operator delete(void* ptr, size_t size) {MemoryPool::Deallocate(ptr, size);}MyObject(int val) : data(val) {}int getData() const { return data; }private:int data;
};int main() {// 測試內存池MyObject* obj1 = new MyObject(10);MyObject* obj2 = new MyObject(20);std::cout << "obj1 data: " << obj1->getData() << std::endl;std::cout << "obj2 data: " << obj2->getData() << std::endl;delete obj1;delete obj2;// 驗證內存回收后重用MyObject* obj3 = new MyObject(30);std::cout << "obj3 data: " << obj3->getData() << std::endl;delete obj3;return 0;
}

代碼解析:

  • MemoryPool管理空閑內存塊,Allocate優先使用空閑列表
  • MyObject重載newdelete,使用自定義內存池
  • main函數測試內存分配、釋放和重用

編譯運行:

g++ -std=c++11 mem_pool.cpp -o mem_pool && ./mem_pool

二、自定義分配器

重點知識

  1. STL容器默認分配器性能問題
    • 頻繁小內存分配效率低
  2. 實現自定義分配器
    • 必須提供allocatedeallocate等方法
    • 需要處理類型定義和模板參數

代碼示例:固定大小內存分配器

#include <iostream>
#include <vector>
#include <memory>template <typename T>
class FixedAllocator {
public:using value_type = T;FixedAllocator() = default;template <typename U>FixedAllocator(const FixedAllocator<U>&) {}T* allocate(size_t n) {if (n != 1) {throw std::bad_alloc(); // 僅支持單對象分配}return static_cast<T*>(::operator new(sizeof(T)));}void deallocate(T* p, size_t n) {::operator delete(p);}
};// 允許不同模板實例間的轉換
template <typename T, typename U>
bool operator==(const FixedAllocator<T>&, const FixedAllocator<U>&) {return true;
}int main() {std::vector<int, FixedAllocator<int>> vec;for (int i = 0; i < 5; ++i) {vec.push_back(i);}std::cout << "Vector elements: ";for (auto v : vec) {std::cout << v << " ";}std::cout << std::endl;return 0;
}

代碼解析:

  • FixedAllocator實現固定大小的內存分配
  • std::vector結合使用,減少內存分配次數
  • main測試自定義分配器的容器使用

編譯運行:

g++ -std=c++11 custom_allocator.cpp -o custom_allocator && ./custom_allocator

三、智能指針優化

重點知識
  1. std::make_shared vs new
    • make_shared合并控制塊和對象內存,提升局部性
  2. 避免循環引用
    • 使用weak_ptr打破循環
代碼示例:智能指針性能對比
#include <iostream>
#include <memory>
#include <chrono>class HeavyObject {
public:HeavyObject() { data = new int[1000]; }~HeavyObject() { delete[] data; }
private:int* data;
};void test_make_shared() {auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < 10000; ++i) {auto p = std::make_shared<HeavyObject>();}auto end = std::chrono::high_resolution_clock::now();std::cout << "make_shared time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()<< " ms\n";
}void test_new_shared() {auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < 10000; ++i) {auto p = std::shared_ptr<HeavyObject>(new HeavyObject);}auto end = std::chrono::high_resolution_clock::now();std::cout << "new+shared_ptr time: "<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()<< " ms\n";
}int main() {test_make_shared();test_new_shared();return 0;
}

代碼解析:

  • 對比make_shared和直接new的性能差異
  • HeavyObject模擬大對象分配
  • 使用高精度計時器測量執行時間

編譯運行:

g++ -std=c++11 smart_ptr.cpp -o smart_ptr && ./smart_ptr

四、性能優化關鍵點總結

  1. 減少系統調用
    • 預分配內存池
    • 批量分配代替單次分配
  2. 提高緩存命中率
    • 對象連續存儲(如std::vector
    • 使用make_shared合并內存塊
  3. 線程安全考慮
    • 多線程環境需加鎖(示例未展示,但實際項目需注意)
  4. 自定義分配器適用場景
    • 頻繁小對象分配
    • 特定大小的對象分配

核心知識點總結:

  1. C++內存管理API(new/delete, operator new/delete)
  2. 自定義內存分配器的設計與實現
  3. 類專用內存管理器(per-class allocator)
  4. 內存池技術(memory pool)
  5. 智能指針與所有權管理
  6. 移動語義與右值引用優化
  7. 內存對齊與緩存優化
  8. 內存碎片管理策略
  9. 多線程環境下的內存管理
  10. 標準庫容器內存分配策略

多選題

  1. 關于C++內存管理API,哪些說法正確?
    A. operator new可以重載實現自定義內存分配策略
    B. delete表達式會自動調用析構函數并釋放內存
    C. placement new不會分配內存,只在已分配內存上構造對象
    D. ::operator new(size_t)會觸發構造函數調用

  2. 以下哪些方法可以有效減少動態內存分配?
    A. 使用std::make_shared替代new
    B. 預分配內存并復用內存塊
    C. 使用std::vector的reserve方法
    D. 優先使用棧分配對象

  3. 關于類專用內存管理器,正確的是:
    A. 需要重載類的operator new和operator delete
    B. 可以避免內存碎片問題
    C. 適用于頻繁創建銷毀的小對象
    D. 必須使用單例模式實現

  4. 選擇內存池技術的主要優勢包括:
    A. 減少內存分配/釋放的系統調用開銷
    B. 提高緩存局部性
    C. 完全消除內存泄漏風險
    D. 支持任意大小的內存分配

  5. 關于std::allocator,正確的是:
    A. 可以通過rebind模板適配不同類型
    B. 分配的內存總是按字節對齊
    C. 默認實現使用malloc/free
    D. 可以完全避免內存碎片

  6. 移動語義對內存管理的優化體現在:
    A. 避免不必要的深拷貝
    B. 允許資源所有權的轉移
    C. 完全替代拷貝構造函數
    D. 只能在模板元編程中使用

  7. 多線程環境下內存管理需要注意:
    A. 使用線程局部存儲(TLS)分配器
    B. 避免虛假共享(false sharing)
    C. 必須使用鎖保護所有分配操作
    D. 優先使用無鎖數據結構

  8. 關于內存對齊優化,正確的是:
    A. alignas關鍵字可以指定對象對齊方式
    B. SIMD指令需要特殊內存對齊
    C. 錯誤對齊會導致性能下降
    D. 所有平臺默認對齊方式相同

  9. 智能指針的內存管理策略包括:
    A. std::shared_ptr使用引用計數
    B. std::unique_ptr支持拷貝語義
    C. std::weak_ptr用于打破循環引用
    D. make_shared比直接new更高效

  10. 減少內存復制的有效方法有:
    A. 使用移動構造函數
    B. 實現寫時復制(COW)
    C. 優先傳遞const引用
    D. 所有返回對象都使用RVO


設計題

  1. 實現固定大小內存池

    // 要求:
    // 1. 支持固定大小的內存塊分配
    // 2. 內存池預分配大塊內存管理
    // 3. 線程安全
    // 4. 提供性能對比測試
    
  2. 優化std::list的內存分配

    // 要求:
    // 1. 為std::list設計專用分配器
    // 2. 每次批量分配多個節點內存
    // 3. 支持動態調整批量大小
    // 4. 驗證內存使用效率提升
    
  3. 無鎖內存分配器設計

    // 要求:
    // 1. 實現基于原子操作的內存分配
    // 2. 支持多線程并發分配
    // 3. 避免使用mutex鎖
    // 4. 測試并發性能指標
    
  4. 對象池模板實現

    // 要求:
    // 1. 模板化設計支持任意類型
    // 2. 對象復用避免重復構造
    // 3. 自動回收機制
    // 4. 測試對象創建性能提升
    
  5. 智能指針自定義刪除器優化

    // 要求:
    // 1. 實現內存池綁定的刪除器
    // 2. 與std::unique_ptr集成
    // 3. 支持不同內存池實例
    // 4. 驗證內存回收正確性
    

答案與詳解

多選題答案
  1. ABC
    D錯誤:operator new只分配內存,不調用構造函數

  2. ABCD
    所有選項均為有效減少動態分配的方法

  3. ABC
    D錯誤:單例模式不是必須的

  4. AB
    C錯誤:不能完全消除泄漏;D錯誤:固定大小

  5. AC
    B錯誤:對齊由實現決定;D錯誤:仍可能產生碎片

  6. AB
    C錯誤:不能完全替代;D錯誤:通用特性

  7. ABD
    C錯誤:無鎖設計不需要鎖

  8. ABC
    D錯誤:不同平臺對齊要求不同

  9. ACD
    B錯誤:unique_ptr不可拷貝

  10. ABCD
    所有選項均為有效方法


設計題示例答案(第1題)

固定大小內存池實現

#include <iostream>
#include <vector>
#include <memory>
#include <chrono>template <size_t BlockSize>
class FixedMemoryPool {struct Block {char data[BlockSize];Block* next;};Block* freeList = nullptr;std::vector<std::unique_ptr<char[]>> chunks;public:void* allocate() {if (!freeList) {const size_t chunk_size = 1024;auto chunk = std::make_unique<char[]>(chunk_size * BlockSize);chunks.push_back(std::move(chunk));for (size_t i = 0; i < chunk_size; ++i) {Block* blk = reinterpret_cast<Block*>(chunks.back().get() + i * BlockSize);blk->next = freeList;freeList = blk;}}void* result = freeList;freeList = freeList->next;return result;}void deallocate(void* ptr) {Block* blk = static_cast<Block*>(ptr);blk->next = freeList;freeList = blk;}
};struct MyObject {int data[128];
};void test_performance() {const int iterations = 100000;// 測試標準分配auto start_std = std::chrono::high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {auto ptr = new MyObject;delete ptr;}auto end_std = std::chrono::high_resolution_clock::now();// 測試內存池FixedMemoryPool<sizeof(MyObject)> pool;auto start_pool = std::chrono::high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {auto ptr = pool.allocate();pool.deallocate(ptr);}auto end_pool = std::chrono::high_resolution_clock::now();auto std_time = std::chrono::duration_cast<std::chrono::microseconds>(end_std - start_std).count();auto pool_time = std::chrono::duration_cast<std::chrono::microseconds>(end_pool - start_pool).count();std::cout << "Standard alloc: " << std_time << "μs\n"<< "Pool alloc:     " << pool_time << "μs\n"<< "Performance ratio: " << static_cast<double>(std_time)/pool_time << "x\n";
}int main() {test_performance();return 0;
}

測試結果示例:

Standard alloc: 5432μs
Pool alloc:     127μs
Performance ratio: 42.75x

實現要點:

  1. 使用鏈表管理空閑塊
  2. 批量預分配內存塊(chunk)
  3. 分配/釋放操作O(1)時間復雜度
  4. 線程安全需要額外加鎖(示例未包含)
  5. 顯著提升小對象分配性能

其他設計題目的答案, 稍后補充
其他設計題需要類似的結構,針對特定問題設計解決方案,并通過性能測試驗證優化效果。每個實現應包含:

  • 核心數據結構和算法
  • 內存管理策略
  • 線程安全機制(如果需要)
  • 性能測試對比
  • 正確性驗證測試

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

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

相關文章

python筆記之函數

函數初探 python在要寫出函數很簡單&#xff0c;通過關鍵字def即可寫出&#xff0c;簡單示例如下 def add(a, b):return ab 以上即可以定義出一個簡單的函數&#xff1a;接收兩個變量a和b&#xff0c;返回a和b相加的結果&#xff0c;當然這么說也不全對&#xff0c;原因就是…

【服務器操作指南 - GPU 使用與文件傳輸】輕松掌握 GPU 狀態查看和服務器文件傳輸技巧

0. 引言 在使用服務器時&#xff0c;高效管理 GPU 和文件傳輸是兩項不可或缺的技能。 本指南旨在幫助您快速掌握服務器環境下的 GPU 使用狀態監測方法&#xff0c;并簡要介紹如何在服務器之間進行文件傳輸操作。 1. 查看服務器上的 gpu 使用狀態 1.1 安裝 gpustat 這條指令…

0330-YYYY-MM-DD格式日期比較大小

最簡單的&#xff08;python&#xff09; from datetime import datetime def compare_time(time1,time2): time1_t datetime.strptime(time1,“%Y-%m-%d”) time2_t datetime.strptime(time2,“%Y-%m-%d”) if time1_t < time2_t: return time1_t elif time1_t > ti…

QFlightInstruments飛行儀表控件庫

QFlightInstruments 是一個開源的飛行儀表控件庫&#xff0c;專為基于 Qt 的應用程序設計。它提供了一系列仿真實飛機儀表的組件&#xff0c;適用于飛行模擬軟件、航空電子系統或任何需要高仿真飛行儀表顯示的項目。 主要功能 高仿真飛行儀表&#xff1a;包括空速表、高度表、…

VSCode 市場發現惡意擴展正在傳播勒索軟件!

在VSCode 市場中發現了兩個隱藏著勒索軟件的惡意擴展。其中一個于去年 10 月出現在微軟商店&#xff0c;但很長時間沒有引起注意。 這些是擴展ahban.shiba 和 ahban.cychelloworld&#xff0c;目前已從商店中刪除。 此外&#xff0c;ahban.cychelloworld 擴展于 2024 年 10 月…

國信華源攜AI+水利創新成果亮相第十五屆防汛抗旱信息化技術交流會

直擊展會現場 近日&#xff0c;以“人工智能賦能防汛抗旱 融合創新共御極端災害”為主題的第十五屆防汛抗旱信息化技術交流會在河南鄭州召開。作為水旱災害防御領域的專精企業&#xff0c;北京國信華源科技有限公司攜自主研發的入戶叫應預警系統及覆蓋防汛抗旱全鏈條的智慧化場…

MATLAB語言的鏈表反轉

MATLAB語言的鏈表反轉 鏈表是一種常見的數據結構&#xff0c;與數組相比&#xff0c;鏈表在插入和刪除操作方面具有更高的靈活性。然而&#xff0c;鏈表的一些操作&#xff0c;比如反轉鏈表&#xff0c;對一些初學者來說可能是一個挑戰。本篇文章將重點討論如何使用MATLAB語言…

Oracle數據庫數據編程SQL<2.2 DDL 視圖、序列>

目錄 一、Oracle 視圖(Views) &#xff08;一&#xff09; Oracle 視圖特點 &#xff08;二&#xff09;Oracle 視圖創建語法 關鍵參數&#xff1a; &#xff08;三&#xff09;Oracle 視圖類型 1、普通視圖 2、連接視圖&#xff08;可更新&#xff09; 3、對象視圖 4…

QtAdvancedStylesheets使用

QtAdvancedStylesheets 是一個基于 Qt Widgets 的樣式表(QSS)增強庫,允許開發者通過類似 CSS 的方式深度定制 Qt 應用程序的界面風格,支持動態主題切換、動畫效果和復雜控件樣式設計。 1. 核心功能 高級樣式表支持 使用 CSS-like 語法美化 Qt Widgets(如 QPushButton、Q…

QtAV入門

QtAV 是一個基于 FFmpeg 和 Qt 的高性能多媒體播放框架,提供強大的音視頻解碼、渲染和處理能力,適合開發跨平臺的播放器、視頻編輯和流媒體應用。 1. 核心功能 多格式支持 支持 H.264/H.265、VP9、AV1 等視頻編碼。 支持 MP3、AAC、Opus 等音頻編碼。 封裝格式:MP4、MKV、…

[ C++ ] | C++11 從左值引用到右值引用

&#xff08;目錄占位&#xff09; 1. 前言&#xff1a; C 11 是在 C 98 之后又一個變化比較大的標準。為C增加了很多東西&#xff0c;其中有一部分是有用的&#xff0c;有一部分是我自認為作用不是很大東西。這一章呢&#xff1f;我們就來說說C11我&#xff0c;我認為對性能…

基于MCU實現的電機轉速精確控制方案:軟件設計與實現

本文將詳細介紹一篇基于微控制器&#xff08;MCU&#xff09;的電機轉速精確控制的軟件方案。通過采樣PWM信號控制和ADC采樣技術&#xff0c;結合PID閉環控制算法&#xff0c;實現了電機轉速的高效、穩定調節。以下是軟件方案流程圖&#xff0c;下文將對其進行展開講解。 原圖太…

Jmeter觸發腳本備份

JMeter 在以下情況會觸發腳本備份&#xff1a; 手動保存測試計劃時&#xff1a;如果測試計劃有未保存的修改&#xff0c;當用戶手動保存測試計劃&#xff08;腳本&#xff09;時&#xff0c;JMeter 都會自動將當前腳本備份到${JMETER_HOME}/backups文件夾下。 關閉 JMeter 時…

AI人工智能-PyCharm的介紹安裝應用

下載與安裝 創建python項目 項目路徑&#xff1a;C:\Users\miloq\Desktop\python_project 配置環境 提前找到conda配置的python-base路徑 配置conda環境 運行項目 運行結果

Flink內存模型--flink1.19.1

Flink 的 JobManager 和 TaskManager 在內存分配上有不同的職責和結構。以下是兩者的內存分類及詳細說明&#xff1a; 一、JobManager 內存分類 JobManager 主要負責作業調度、協調&#xff08;如 Checkpoint 協調&#xff09;、資源管理等&#xff0c;其內存需求相對較低&…

華為數字化轉型-方法篇

1 方法篇-3-愿景驅動的數字化轉型規劃 1.2 業務戰略是數字化轉型的龍頭 1.3 數字時代&#xff0c;企業需要適時地調整業務戰略 1.3.1 引入數字化商業模式 引入數字化商業模式包括改變與客戶做生意的方式&#xff0c;改變銷售的渠道&#xff0c;基于產業互聯網重新定位與行 業…

常用的排序算法------練習4

1. 題目 2. 思路和題解 這道題是很經典的荷蘭國旗問題&#xff0c;根據題目意思&#xff0c;要對這個數組按照顏色排序&#xff0c;而此時現在的紅、白、藍三個顏色分別對應0&#xff0c;1&#xff0c;2&#xff0c;因此可以想到使用冒泡排序對該數組進行排序。 代碼如下&…

傳統神經網絡、CNN與RNN

在網絡上找了很多關于深度學習的資料&#xff0c;也總結了一點小心得&#xff0c;于是就有了下面這篇文章。這里內容較為簡單&#xff0c;適合初學者查看&#xff0c;所以大佬看到這里就可以走了。 話不多說&#xff0c;上圖 #mermaid-svg-Z3k5YhiQ2o5AnvZE {font-family:&quo…

1371. 貨幣系統-dp背包問題

給定 V種貨幣&#xff08;單位&#xff1a;元&#xff09;&#xff0c;每種貨幣使用的次數不限。 不同種類的貨幣&#xff0c;面值可能是相同的。 現在&#xff0c;要你用這 V種貨幣湊出 N 元錢&#xff0c;請問共有多少種不同的湊法。 輸入格式 第一行包含兩個整數 V 和 N…

python和Java的區別

Python和Java是兩種流行的編程語言&#xff0c;它們之間有一些重要的區別&#xff1a; 語法&#xff1a;Python是一種動態類型的腳本語言&#xff0c;語法簡潔明了&#xff0c;通常使用縮進來表示代碼塊。Java是一種靜態類型的編程語言&#xff0c;語法更為嚴格&#xff0c;需要…