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

news2025/3/31 4:53:47

优化内存管理

      • 一、内存管理基础概念
      • 二、自定义分配器
      • 三、智能指针优化
        • 重点知识
        • 代码示例:智能指针性能对比
      • 四、性能优化关键点总结
      • 多选题
      • 设计题
      • 答案与详解
        • 多选题答案
        • 设计题示例答案(第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.coloradmin.cn/o/2323064.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络通信安全】基于华为 eNSP 的链路聚合、手工负载分担模式与 LACP 扩展配置 全解析

目录 一、引言 二、链路聚合技术基础 2.1 链路聚合的定义与作用 2.2 链路聚合的工作原理 2.3 链路聚合的模式分类 三、华为 eNSP 简介 3.1 eNSP 的概述 3.2 eNSP 的安装与配置 3.2.1 安装环境要求 3.2.2 安装步骤 3.2.3 配置虚拟网卡 四、手工负载分担模式配置 4.…

Transformer 通关秘籍2:利用 BERT 将文本 token 化

前面两节分别通过两个代码示例展示了模型将文本转换为 token 之后是什么样的&#xff0c;希望你可以对此有一个感性的认识。 本节来简要介绍一下将一个连续的文本转换为 token 序列的大致过程&#xff0c;这个过程被称为分词&#xff0c;也叫 tokenization。 在你没了解这方面…

网络运维学习笔记(DeepSeek优化版) 024 HCIP-Datacom OSPF域内路由计算

文章目录 OSPF域内路由计算&#xff1a;单区域的路由计算一、OSPF单区域路由计算原理二、1类LSA详解2.1 1类LSA的作用与结构2.2 1类LSA的四种链路类型 三、OSPF路由表生成验证3.1 查看LSDB3.2 查看OSPF路由表3.3 查看全局路由表 四、2类LSA详解4.1 2类LSA的作用与生成条件4.2 2…

【云馨AI-大模型】自动化部署Dify 1.1.2,无需科学上网,Linux环境轻松实现,附Docker离线安装等

Dify介绍 官网&#xff1a;https://dify.ai/zh生成式 AI 应用创新引擎开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力&#xff0c;轻松构建和运营生成式 AI 原生应用。 Dify安装脚本 目录创建 mkdir -p /data/yunxinai &&a…

CUDA 学习(2)——CUDA 介绍

GeForce 256 是英伟达 1999 年开发的第一个 GPU&#xff0c;最初用作显示器上渲染高端图形&#xff0c;只用于像素计算。 在早期&#xff0c;OpenGL 和 DirectX 等图形 API 是与 GPU 唯一的交互方式。后来&#xff0c;人们意识到 GPU 除了用于渲染图形图像外&#xff0c;还可以…

棱镜七彩受邀出席“供应链安全国家标准贯标应用深度行”活动并做主题分享

近日&#xff0c;“供应链安全国家标准贯标应用深度行”活动在北京顺利举办&#xff0c;此次活动汇聚了行业内的众多专家和企业代表&#xff0c;深入探讨了供应链安全国家标准的制定与实施路径。棱镜七彩副总裁黄浩东受邀出席&#xff0c;并发表了题为《国家标准实施路径下的企…

系统转换、系统维护、净室软件工程、构件软件工程(高软51)

系列文章目录 系统转换、系统维护、净室软件工程、构件软件工程 文章目录 系列文章目录前言一、系统转换二、系统维护三、净室软件工程四、基于构件的软件工程总结 前言 本节讲明遗留系统的系统转换、系统维护、净室软件工程、基于构件软件工程相关知识。 一、系统转换 就是讲…

联核防爆无人叉车:高危环境中的安全搬运守护者

联核防爆AGV无人叉车是专为易燃易爆环境设计的智能搬运设备&#xff0c;其特点、功能与应用场景均围绕“安全”与“智能”核心展开&#xff1a;联核科技官网-AGV叉车十大品牌-无人叉车厂家-自动化叉车-智能搬运码垛机器人-智能叉车系统解决方案专家 一、核心特点 防爆设计电气…

23种设计模式-责任链(Chain of Responsibility)设计模式

责任链设计模式 &#x1f6a9;什么是责任链设计模式&#xff1f;&#x1f6a9;责任链设计模式的特点&#x1f6a9;责任链设计模式的结构&#x1f6a9;责任链设计模式的优缺点&#x1f6a9;责任链设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是…

Linux使用集群服务器查看已安装conda环境,且环境名无显示、系统环境混乱等问题

一、问题 在使用集群服务器前可以查看导入&#xff0c;module load不需要安装。我都是自己重新下载Anaconda3-2024.10-1-Linux-x86_64.sh&#xff0c;然后安装&#xff0c;导致混乱。下面是情况 1.创建的环境名跑到目录下了 2.多个base,且有个base无显示 二、解决办法 1.删…

python蓝桥杯刷题的重难点知识笔记

1、datetime模块 datetime.date&#xff1a;代表日期&#xff0c;包含年、月、日信息。datetime.time&#xff1a;代表时间&#xff0c;包含时、分、秒、微秒信息。datetime.datetime&#xff1a;结合了日期和时间&#xff0c;包含年、月、日、时、分、秒、微秒信息。datetime.…

Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现

一、前言 在移动互联网蓬勃发展的今天&#xff0c;视频播放功能已成为众多Android应用的核心特性之一。面对多样化的视频格式和传输协议&#xff0c;开发一款高效、稳定的视频播放器是许多开发者追求的目标。FLV&#xff08;Flash Video&#xff09;格式&#xff0c;尽管随着H…

极光优化PLO-Transformer-LSTM多变量时序

极光优化算法(PLO)于2024年8月发表于SCI期刊《Neurocomputing》&#xff0c;利用算法极光优化算法PLO优化Transformer-LSTM模型&#xff0c;同时提供与未优化模型的对比&#xff0c;包含柱状图、两张雷达图、二维散点图等等。 &#xff08;一&#xff09;LSTM模型LSTM是一种在时…

基于javaweb的SpringBoot智能无人仓库管理设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

python处理音频相关的库

1 音频信号采集与播放 pyaudio import sys import pyaudio import wave import timeCHUNK 1024 FORMAT pyaudio.paInt16 CHANNELS 1#仅支持单声道 RATE 16000 RECORD_SECONDS 3#更改录音时长#录音函数&#xff0c;生成wav文件 def record(file_name):try:os.close(file_…

网络爬虫-2:基础与理论

一.同步加载与异步加载 1.1同步加载定义: 页面所有内容一起加载出来,当某一个数据加载有问题,整个页面就不会加载出来(如HiFiNi音乐网站),所以又叫阻塞模式 1.2爬取步骤: 看netword->document 2.1异步加载定义: 数据是分开加载的,当某一份数据有异常时,不影响其他数据…

[项目]基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050配置与读取

基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050 一.芯片介绍二.配置I2C三.编写驱动四.读取任务的测试五.MPU6050六轴数据的校准 一.芯片介绍 芯片应该放置在PCB中间&#xff0c;X Y轴原点&#xff0c;敏感度131表示范围越小越灵敏。理想状态放置在地面上X&#xff0c;Y&#xf…

后端学习day1-Spring(八股)--还剩9个没看

一、Spring 1.请你说说Spring的核心是什么 参考答案 Spring框架包含众多模块&#xff0c;如Core、Testing、Data Access、Web Servlet等&#xff0c;其中Core是整个Spring框架的核心模块。Core模块提供了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能&#xff0c;…

【赵渝强老师】在Docker中运行达梦数据库

Docker是一个客户端服务器&#xff08;Client-Server&#xff09;架构。Docker客户端和Docker守护进程交流&#xff0c;而Docker的守护进程是运作Docker的核心&#xff0c;起着非常重要的作用&#xff08;如构建、运行和分发Docker容器等&#xff09;。达梦官方提供了DM 8在Doc…

Python电影市场特征:AR模型时间序列趋势预测、热图可视化评分影响分析IMDb数据|附数据代码

原文链接&#xff1a;https://tecdat.cn/?p41214 分析师&#xff1a;Zhiheng Lin 在数字时代&#xff0c;电影产业的数据分析已成为洞察市场趋势与用户偏好的重要工具。本专题合集聚焦印度电影市场&#xff0c;通过IMDb数据集&#xff08;IMDb Movies Dataset&#xff09;的深…