目录
1,堆(heap)使用要点总结
2,栈(stack)使用要点总结
3,RAII思想使用总结
4,常用的优化内存管理技术
4.1,内存池
4.2,智能指针
4.3,内存泄露检测工具
5,C++17 std::pmr内存框架
5.1,为什么需要std::pmr
5.2,std::pmr核心概念
5.3,std::pmr的使用
1,堆(heap)使用要点总结
- 动态分配内存的区域,使用 new 和 delete 或 malloc 和 free 进行内存分配和释放。如果没有手动释放,可能会造成内存泄漏。
int* ptr = new int; // 分配一个int大小的内存
*ptr = 10; // 初始化内存
delete ptr; // 释放内存
//使用new[]创建数组
int* arr = new int[10]; // 分配一个int数组
for (int i = 0; i < 10; ++i) {
arr[i] = i;
}
delete[] arr; // 释放数组
- new 和 delete 是 C++ 中的运算符,malloc 和 free 是 C 语言中的函数,虽然在 C++ 中也可以使用,但通常更推荐使用
new
和delete
,因为它们支持构造和析构操作,这对于对象的生命周期管理至关重要。
int* ptr = (int*)malloc(sizeof(int)); // 分配内存
*ptr = 10; // 初始化内存
free(ptr); // 释放内存
2,栈(stack)使用要点总结
- 栈内存管理是函数调用过程中用于存储本地变量和调用数据的区域。
- 栈是后进先出(LIFO)的结构。在大多数计算机架构中,栈从高地址向低地址增长。
- 每次函数调用时,会将参数和返回地址压入栈中。函数执行结束时,栈指针会复位到调用该函数前的位置,释放该函数使用的栈空间。
- 栈上的内存分配和释放非常高效和简单,只需移动栈指针即可。
3,RAII思想使用总结
- RAII 是 C++ 中管理资源的一种惯用手法,保证资源在对象生命周期内的正确分配和释放。
- 对象可以存储在栈上或堆上,但在许多情况下,对象不应存在栈上,例如对象非常大或大小在编译时无法确定。
- RAII 的核心思想是将资源的获取与对象的生命周期绑定,在对象的构造函数中获取资源,并在析构函数中释放资源。
- RAII思想除了广泛应用于内存管理,还可用于文件句柄、同步锁等关键资源的管理。
#include <fstream>
class FileHandle {
public:
FileHandle(const char* filename) {
file_.open(filename);
}
~FileHandle() {
if (file_.is_open()) {
file_.close();
}
}
// 禁止复制构造和赋值操作
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
private:
std::ofstream file_;
};
在上述示例中,FileHandle
类在构造函数中打开文件,并在析构函数中关闭文件。这样,只要FileHandle
对象的生命周期结束,文件就会自动关闭,从而避免了资源泄漏。
4,常用的优化内存管理技术
4.1,内存池
内存池是一种优化动态内存分配的技术,它预先分配一大块内存,并从中分配小块内存给请求者。这样可以减少内存碎片,并提高内存分配的效率。
class MemoryPool {
public:
MemoryPool(size_t poolSize) {
pool_ = new char[poolSize];
freeList_ = pool_;
}
~MemoryPool() {
delete[] pool_;
}
void* allocate(size_t size) {
// 实现内存分配逻辑
}
void deallocate(void* pointer) {
// 实现内存释放逻辑
}
private:
char* pool_;
char* freeList_;
};
4.2,智能指针
智能指针是一种自动管理动态分配内存的类,可以自动释放内存,避免内存泄漏。C++11引入了几种智能指针,如std::unique_ptr
、std::shared_ptr
和std::weak_ptr
。
#include <memory>
std::unique_ptr<int> uniquePtr(new int(10));
std::shared_ptr<int> sharedPtr(new int(20));
4.3,内存泄露检测工具
C++有多种工具可以帮助检测内存泄漏,如Valgrind、AddressSanitizer等。这些工具可以在程序运行时监控内存分配和释放,帮助我们定位内存泄漏的位置,快速排查解决问题。
- Valgrind
开源框架,通过模拟CPU环境来检测程序中的内存问题。Memcheck是最常用的模块,用于检测内存泄漏、内存越界等问题。运行命令如下
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./program_name
优点:
能够检测多种内存问题,如内存泄漏、内存越界等;
提供详细的错误报告,包括泄漏的大小和在代码中的位置。
缺点:
运行被检测的程序时会显著变慢。
- AddressSanitizer(ASAN)
ASAN 是一个快速的内存错误检测器,它可以检测使用已释放内存、堆内存越界、栈内存越界等问题。它通过编译时插桩来检测错误。使用时需要在编译语句中增加开启:
g++ -fsanitize=address -g test_program.cpp -o test_program
优点:
检测速度快,对程序性能的影响较小;
可以与现有的调试工具一起使用。
缺点:
可能会增加编译后的程序大小。
5,C++17 std::pmr内存框架
C++17 引入的 std::pmr
(Polymorphic Memory Resource多态内存资源)框架,是对C++内存管理系统的一个重要扩展,使得内存分配器可以更加灵活地管理内存分配策略,特别是在容器等数据结构中,通过这种方法,可以为不同的容器指定不同的内存资源,提供更灵活的内存管理选项。
5.1,为什么需要std::pmr
在 std::vector、std::string
等标准库容器中,默认情况下内存分配是通过全局的 new
/delete
来进行的,无法轻松地更改这些容器的内存分配策略。例如:
- 提高性能:在某些高性能场景下,可能需要对容器中的内存进行优化,比如使用内存池或固定大小的内存块。
- 减少内存碎片:特别是在嵌入式系统中,内存池能够帮助减少动态内存分配带来的碎片化问题。
- 共享内存资源:多个容器可以共享同一块内存资源,而无需各自管理内存,这样能减少内存使用量。
C++17 的 std::pmr
提供了一种灵活的方式,使得我们可以控制内存的分配方式,从而优化内存的使用和性能 。
5.2,std::pmr核心概念
std::pmr
框架中引入了以下几个重要概念:
Memory Resource(内存资源):负责实际的内存分配与释放操作。
std::pmr::new_delete_resource()
:默认的内存资源,使用::operator new
和::operator delete
进行内存管理。std::pmr::monotonic_buffer_resource
:单调分配器,用于分配一块大内存并逐步使用,不支持个别对象的释放,适合短生命周期的对象。std::pmr::unsynchronized_pool_resource
:无锁内存池,适合单线程环境。std::pmr::synchronized_pool_resource
:线程安全的内存池。
Polymorphic Allocator(多态分配器):是一种可以在运行时绑定不同内存资源的分配器,允许在使用标准库容器时自定义内存分配策略。
Memory Resource Hierarchy(内存资源层次):std::pmr
提供了几个预定义的内存资源,并且用户可以定义自己的内存资源。
5.3,std::pmr的使用
std::pmr::polymorphic_allocator
是 std::pmr
框架中的核心类之一,它可以用作标准库容器的自定义分配器。这允许程序员使用不同的内存资源管理容器的内存分配。
#include <iostream>
#include <vector>
#include <memory_resource> // std::pmr 需要包含此头文件
int main() {
// 准备一个内存缓冲区,大小为1024字节
char buffer[1024];
// 创建一个 monotonic_buffer_resource,使用上面的 buffer 作为内存池
std::pmr::monotonic_buffer_resource resource(buffer, sizeof(buffer));
// 使用该资源创建 polymorphic_allocator
std::pmr::polymorphic_allocator<int> allocator(&resource);
// 使用该 allocator 创建 std::pmr 容器(如 vector)
std::pmr::vector<int> vec(allocator);
// 向 vector 中添加一些元素
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
}
// 输出 vector 中的元素
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
// 注意:使用 monotonic_buffer_resource 后,内存不会被单独释放,
// 而是等整个 buffer 被重用或者整个资源被销毁时释放
return 0;
}
在线程安全环境使用std::pmr
#include <iostream>
#include <vector>
#include <memory_resource>
int main() {
// 创建一个线程安全的内存池
std::pmr::synchronized_pool_resource pool;
// 创建分配器,使用 synchronized_pool_resource
std::pmr::polymorphic_allocator<int> allocator(&pool);
// 使用自定义的分配器创建 vector
std::pmr::vector<int> vec(allocator);
// 向 vector 中添加一些元素
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
}
// 输出 vector 中的元素
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}