第十三章:优化内存管理_《C++性能优化指南》_notes
优化内存管理
- 一、内存管理基础概念
- 二、自定义分配器
- 三、智能指针优化
- 重点知识
- 代码示例:智能指针性能对比
- 四、性能优化关键点总结
- 多选题
- 设计题
- 答案与详解
- 多选题答案
- 设计题示例答案(第1题)
一、内存管理基础概念
重点知识
- 动态内存分配开销
new和delete涉及系统调用,频繁操作会导致性能瓶颈- 内存碎片化会降低内存利用率
- 自定义内存管理
- 预分配内存块(内存池)
- 类专属内存管理器
- 自定义分配器
代码示例:类专属内存管理器
#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重载new和delete,使用自定义内存池main函数测试内存分配、释放和重用
编译运行:
g++ -std=c++11 mem_pool.cpp -o mem_pool && ./mem_pool
二、自定义分配器
重点知识
- STL容器默认分配器性能问题
- 频繁小内存分配效率低
- 实现自定义分配器
- 必须提供
allocate、deallocate等方法 - 需要处理类型定义和模板参数
- 必须提供
代码示例:固定大小内存分配器
#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
三、智能指针优化
重点知识
std::make_sharedvsnewmake_shared合并控制块和对象内存,提升局部性
- 避免循环引用
- 使用
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
四、性能优化关键点总结
- 减少系统调用
- 预分配内存池
- 批量分配代替单次分配
- 提高缓存命中率
- 对象连续存储(如
std::vector) - 使用
make_shared合并内存块
- 对象连续存储(如
- 线程安全考虑
- 多线程环境需加锁(示例未展示,但实际项目需注意)
- 自定义分配器适用场景
- 频繁小对象分配
- 特定大小的对象分配
核心知识点总结:
- C++内存管理API(new/delete, operator new/delete)
- 自定义内存分配器的设计与实现
- 类专用内存管理器(per-class allocator)
- 内存池技术(memory pool)
- 智能指针与所有权管理
- 移动语义与右值引用优化
- 内存对齐与缓存优化
- 内存碎片管理策略
- 多线程环境下的内存管理
- 标准库容器内存分配策略
多选题
-
关于C++内存管理API,哪些说法正确?
A. operator new可以重载实现自定义内存分配策略
B. delete表达式会自动调用析构函数并释放内存
C. placement new不会分配内存,只在已分配内存上构造对象
D. ::operator new(size_t)会触发构造函数调用 -
以下哪些方法可以有效减少动态内存分配?
A. 使用std::make_shared替代new
B. 预分配内存并复用内存块
C. 使用std::vector的reserve方法
D. 优先使用栈分配对象 -
关于类专用内存管理器,正确的是:
A. 需要重载类的operator new和operator delete
B. 可以避免内存碎片问题
C. 适用于频繁创建销毁的小对象
D. 必须使用单例模式实现 -
选择内存池技术的主要优势包括:
A. 减少内存分配/释放的系统调用开销
B. 提高缓存局部性
C. 完全消除内存泄漏风险
D. 支持任意大小的内存分配 -
关于std::allocator,正确的是:
A. 可以通过rebind模板适配不同类型
B. 分配的内存总是按字节对齐
C. 默认实现使用malloc/free
D. 可以完全避免内存碎片 -
移动语义对内存管理的优化体现在:
A. 避免不必要的深拷贝
B. 允许资源所有权的转移
C. 完全替代拷贝构造函数
D. 只能在模板元编程中使用 -
多线程环境下内存管理需要注意:
A. 使用线程局部存储(TLS)分配器
B. 避免虚假共享(false sharing)
C. 必须使用锁保护所有分配操作
D. 优先使用无锁数据结构 -
关于内存对齐优化,正确的是:
A. alignas关键字可以指定对象对齐方式
B. SIMD指令需要特殊内存对齐
C. 错误对齐会导致性能下降
D. 所有平台默认对齐方式相同 -
智能指针的内存管理策略包括:
A. std::shared_ptr使用引用计数
B. std::unique_ptr支持拷贝语义
C. std::weak_ptr用于打破循环引用
D. make_shared比直接new更高效 -
减少内存复制的有效方法有:
A. 使用移动构造函数
B. 实现写时复制(COW)
C. 优先传递const引用
D. 所有返回对象都使用RVO
设计题
-
实现固定大小内存池
// 要求: // 1. 支持固定大小的内存块分配 // 2. 内存池预分配大块内存管理 // 3. 线程安全 // 4. 提供性能对比测试 -
优化std::list的内存分配
// 要求: // 1. 为std::list设计专用分配器 // 2. 每次批量分配多个节点内存 // 3. 支持动态调整批量大小 // 4. 验证内存使用效率提升 -
无锁内存分配器设计
// 要求: // 1. 实现基于原子操作的内存分配 // 2. 支持多线程并发分配 // 3. 避免使用mutex锁 // 4. 测试并发性能指标 -
对象池模板实现
// 要求: // 1. 模板化设计支持任意类型 // 2. 对象复用避免重复构造 // 3. 自动回收机制 // 4. 测试对象创建性能提升 -
智能指针自定义删除器优化
// 要求: // 1. 实现内存池绑定的删除器 // 2. 与std::unique_ptr集成 // 3. 支持不同内存池实例 // 4. 验证内存回收正确性
答案与详解
多选题答案
-
ABC
D错误:operator new只分配内存,不调用构造函数 -
ABCD
所有选项均为有效减少动态分配的方法 -
ABC
D错误:单例模式不是必须的 -
AB
C错误:不能完全消除泄漏;D错误:固定大小 -
AC
B错误:对齐由实现决定;D错误:仍可能产生碎片 -
AB
C错误:不能完全替代;D错误:通用特性 -
ABD
C错误:无锁设计不需要锁 -
ABC
D错误:不同平台对齐要求不同 -
ACD
B错误:unique_ptr不可拷贝 -
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
实现要点:
- 使用链表管理空闲块
- 批量预分配内存块(chunk)
- 分配/释放操作O(1)时间复杂度
- 线程安全需要额外加锁(示例未包含)
- 显著提升小对象分配性能
其他设计题目的答案, 稍后补充
其他设计题需要类似的结构,针对特定问题设计解决方案,并通过性能测试验证优化效果。每个实现应包含:
- 核心数据结构和算法
- 内存管理策略
- 线程安全机制(如果需要)
- 性能测试对比
- 正确性验证测试
相关文章:
第十三章:优化内存管理_《C++性能优化指南》_notes
优化内存管理 一、内存管理基础概念二、自定义分配器三、智能指针优化重点知识代码示例:智能指针性能对比 四、性能优化关键点总结多选题设计题答案与详解多选题答案设计题示例答案(第1题) 一、内存管理基础概念 重点知识 动态内存分配开销…...
【网络通信安全】基于华为 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.…...
RK3568笔记八十: Linux 小智AI环境搭建
若该文为原创文章,转载请注明原文出处。 最近小智AI火了,韦老师出了 Linux 小智 AI 聊天机器人 版本,想移植到 RK3568上, 由于和韦老师硬件不同,所以需要交叉编译一些库,为后续移植做准备。 一、环境 1、…...
Transformer 通关秘籍2:利用 BERT 将文本 token 化
前面两节分别通过两个代码示例展示了模型将文本转换为 token 之后是什么样的,希望你可以对此有一个感性的认识。 本节来简要介绍一下将一个连续的文本转换为 token 序列的大致过程,这个过程被称为分词,也叫 tokenization。 在你没了解这方面…...
Spring Boot分布式项目异常处理实战:从崩溃边缘到优雅恢复
当单体应用拆分成分布式系统,异常就像被打开的潘多拉魔盒:RPC调用超时、分布式事务雪崩、第三方接口突然罢工…在最近的电商大促中,我们的系统就经历了这样的至暗时刻。本文将用真实代码示例,展示如何构建分布式异常处理体系。 一…...
Vue3 中使用 Sortablejs 实现拖拽排序功能 序号不更新问题
Vue3 中使用 Sortablejs 实现拖拽排序功能 序号不更新问题 安装依赖 npm install sortablejs --save简单使用 <template><div class"app-container"><div class"table-header"><el-button type"primary" click"hand…...
网络运维学习笔记(DeepSeek优化版) 024 HCIP-Datacom OSPF域内路由计算
文章目录 OSPF域内路由计算:单区域的路由计算一、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介绍 官网:https://dify.ai/zh生成式 AI 应用创新引擎开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力,轻松构建和运营生成式 AI 原生应用。 Dify安装脚本 目录创建 mkdir -p /data/yunxinai &&a…...
Android 简化图片加载与显示——使用Coil和Kotlin封装高效工具类
为了简化使用Coil加载网络图片和GIF的过程,我们可以封装一个工具类。这个工具类将包括初始化ImageLoader的方法、加载图片到ImageView的方法,以及可能的其他便捷方法,如加载圆形图片、设置占位图等。下面是一个示例: 首先&#x…...
CUDA 学习(2)——CUDA 介绍
GeForce 256 是英伟达 1999 年开发的第一个 GPU,最初用作显示器上渲染高端图形,只用于像素计算。 在早期,OpenGL 和 DirectX 等图形 API 是与 GPU 唯一的交互方式。后来,人们意识到 GPU 除了用于渲染图形图像外,还可以…...
棱镜七彩受邀出席“供应链安全国家标准贯标应用深度行”活动并做主题分享
近日,“供应链安全国家标准贯标应用深度行”活动在北京顺利举办,此次活动汇聚了行业内的众多专家和企业代表,深入探讨了供应链安全国家标准的制定与实施路径。棱镜七彩副总裁黄浩东受邀出席,并发表了题为《国家标准实施路径下的企…...
Vue3项目中的.vscode文件夹
.vscode 文件夹主要用于存放与 Visual Studio Code(VS Code)编辑器相关的项目配置文件,这些文件能让项目在 VS Code 里的开发体验更加个性化和高效。 extensions.json 在 .vscode 文件夹中,extensions.json 文件的作用是列出项目…...
系统转换、系统维护、净室软件工程、构件软件工程(高软51)
系列文章目录 系统转换、系统维护、净室软件工程、构件软件工程 文章目录 系列文章目录前言一、系统转换二、系统维护三、净室软件工程四、基于构件的软件工程总结 前言 本节讲明遗留系统的系统转换、系统维护、净室软件工程、基于构件软件工程相关知识。 一、系统转换 就是讲…...
K8S学习之基础四十四:k8s中部署Kibana
在Kubernetes集群中安装Kibana通常涉及使用Helm Chart或直接使用Kubernetes Manifest文件。以下是使用Helm Chart安装Kibana的步骤: 添加Elastic Helm仓库 首先,添加Elastic的Helm仓库: bash 复制 helm repo add elastic https://helm.ela…...
联核防爆无人叉车:高危环境中的安全搬运守护者
联核防爆AGV无人叉车是专为易燃易爆环境设计的智能搬运设备,其特点、功能与应用场景均围绕“安全”与“智能”核心展开:联核科技官网-AGV叉车十大品牌-无人叉车厂家-自动化叉车-智能搬运码垛机器人-智能叉车系统解决方案专家 一、核心特点 防爆设计电气…...
23种设计模式-责任链(Chain of Responsibility)设计模式
责任链设计模式 🚩什么是责任链设计模式?🚩责任链设计模式的特点🚩责任链设计模式的结构🚩责任链设计模式的优缺点🚩责任链设计模式的Java实现🚩代码总结🚩总结 🚩什么是…...
Linux使用集群服务器查看已安装conda环境,且环境名无显示、系统环境混乱等问题
一、问题 在使用集群服务器前可以查看导入,module load不需要安装。我都是自己重新下载Anaconda3-2024.10-1-Linux-x86_64.sh,然后安装,导致混乱。下面是情况 1.创建的环境名跑到目录下了 2.多个base,且有个base无显示 二、解决办法 1.删…...
python蓝桥杯刷题的重难点知识笔记
1、datetime模块 datetime.date:代表日期,包含年、月、日信息。datetime.time:代表时间,包含时、分、秒、微秒信息。datetime.datetime:结合了日期和时间,包含年、月、日、时、分、秒、微秒信息。datetime.…...
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
一、前言 在移动互联网蓬勃发展的今天,视频播放功能已成为众多Android应用的核心特性之一。面对多样化的视频格式和传输协议,开发一款高效、稳定的视频播放器是许多开发者追求的目标。FLV(Flash Video)格式,尽管随着H…...
Redmi Note 11 T pro + 刷入 LinegaOs 22.1 记录 手机已经解锁bl.
Redmi Note 11 T pro 刷入 LinegaOs 22.1 记录 手机已经解锁bl. 获取LIneagaOS源码, 以及https://github.com/xiaomi-mediatek-devs 这个组织提供的代码,非常感谢 环境要求: ubuntu 22.04 需要准备的依赖 sudo apt install git curl vim…...
《Operating System Concepts》阅读笔记:p483-p488
《Operating System Concepts》学习第 40 天,p483-p488 总结,总计 6 页。 一、技术总结 1.object storage (1)object storage 管理软件 Hadoop file system(HDFS)、Ceph。 二、英语总结(生词:1) 1.commodity (1)commodity: com-(“tog…...
极光优化PLO-Transformer-LSTM多变量时序
极光优化算法(PLO)于2024年8月发表于SCI期刊《Neurocomputing》,利用算法极光优化算法PLO优化Transformer-LSTM模型,同时提供与未优化模型的对比,包含柱状图、两张雷达图、二维散点图等等。 (一)LSTM模型LSTM是一种在时…...
Android开发:基于 Kotlin 协程的设备指令控制工具类设计与实现
在安卓开发中,设备控制是一个常见的需求。本文将介绍如何使用 Kotlin 协程实现一个高效、健壮的设备指令控制工具类。该工具类支持指令队列、重试机制、状态管理等功能,并适配安卓平台,确保生命周期管理和主线程安全性。通过本文,…...
SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景
以下是 SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景: 数据类型类别数据类型解释内存占用适用场景整数类型bigint用于存储范围较大的整数,范围是 -2^63 (-9,223,372,036,854,775,808) 到 2^63-1 (9,223,372,036,854,775,807)8 字节需要…...
Android Kotlin 权限工具类封装:简化动态权限管理
在 Android 开发中,动态权限管理是一个常见的需求,尤其是在高版本 Android 系统中,权限管理变得更加严格和复杂。为了简化权限申请的流程,减少重复代码,本文将介绍如何使用 Kotlin 封装一个高效、易用的权限工具类。 权…...
数据结构每日一题day3(顺序表)★★★★★
题目描述:顺序表L的元素递增有序排列,设计一个算法在插入元素x后保持该顺序表仍然递增有序排列,插入成功后返回插入元素所在位置,不成功返回-1 算法思想:在递增有序的顺序表中插入元素 x 并保持有序性,步骤如下: 合法…...
Git合并删除原理
如果有 A 分支,从 A 分支上新建 B 分支,B 分支做出修改合并到 A 分支,然后删除 B 分支,A 分支还有没有 B 分支修改的内容 关键原理: 合并的本质是提交历史的整合 1. 合并操作会将 B 的修改永久写入 A 的历史 当 …...
Git 是什么
第一步:想象一个场景——写作文的烦恼 假设你在电脑上写一篇作文,反复修改了好几次。突然发现 改错了 想回到之前的某版,但你已经覆盖保存了。这时候你可能会想: 😭 “要是能回到昨天的版本就好了!”&a…...
基于javaweb的SpringBoot智能无人仓库管理设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
python处理音频相关的库
1 音频信号采集与播放 pyaudio import sys import pyaudio import wave import timeCHUNK 1024 FORMAT pyaudio.paInt16 CHANNELS 1#仅支持单声道 RATE 16000 RECORD_SECONDS 3#更改录音时长#录音函数,生成wav文件 def record(file_name):try:os.close(file_…...
