C++多线程环境中进行内存分配跟踪的接口类设计(全局重载new/delete操作符)

本文主要是介绍C++多线程环境中进行内存分配跟踪的接口类设计(全局重载new/delete操作符),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

通过全局重载newdelete操作符,实现堆区空间的分配和释放的跟踪记录

// Memory.h
#if TRACK_MEMORY
#ifdef PLATFORM_WINDOWS_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(size_t size);_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size);_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(size_t size, const char* desc);_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size, const char* desc);_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(size_t size, const char* file, int line);_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size, const char* file, int line);void __CRTDECL operator delete(void* memory);
void __CRTDECL operator delete(void* memory, const char* desc);
void __CRTDECL operator delete(void* memory, const char* file, int line);
void __CRTDECL operator delete[](void* memory);
void __CRTDECL operator delete[](void* memory, const char* desc);
void __CRTDECL operator delete[](void* memory, const char* file, int line);#define hnew new(__FILE__, __LINE__)	// 源文件、行号,用于跟踪进行内存分配的位置
#define hdelete delete#else#warning "Memory tracking not available on non-Windows platform"
#define hnew new
#define hdelete delete#endif#else#define hnew new
#define hdelete delete#endif
// Memory.cpp
#if TRACK_MEMORY && PLATFORM_WINDOWS// windows平台的MSVC编译器的标注和属性
_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR	
void* __CRTDECL operator new(size_t size)
{return Allocator::Allocate(size);	// 分配一块大小为 size 字节的内存。
}_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size)
{return Allocator::Allocate(size);	
}_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(size_t size, const char* desc)
{return Allocator::Allocate(size, desc);	// 分配一块大小为 size 字节的内存,并附带一个描述字符串。
}_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size, const char* desc)
{return Allocator::Allocate(size, desc);
}_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(size_t size, const char* file, int line)
{return Allocator::Allocate(size, file, line);	// 分配一块大小为 size 字节的内存,并记录文件名和行号。
}_NODISCARD _Ret_notnull_ _Post_writable_byte_size_(size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](size_t size, const char* file, int line)
{return Allocator::Allocate(size, file, line);
}void __CRTDECL operator delete(void* memory)
{return Allocator::Free(memory);
}void __CRTDECL operator delete(void* memory, const char* desc)
{return Allocator::Free(memory);
}void __CRTDECL operator delete(void* memory, const char* file, int line)
{return Allocator::Free(memory);
}void __CRTDECL operator delete[](void* memory)
{return Allocator::Free(memory);
}void __CRTDECL operator delete[](void* memory, const char* desc)
{return Allocator::Free(memory);
}void __CRTDECL operator delete[](void* memory, const char* file, int line)
{return Allocator::Free(memory);
}#endif

自定义内存分配接口

// Memory.h
#pragma once#include <map>
#include <mutex>// 用于记录整个程序内存分配的情况
struct AllocationStats
{size_t TotalAllocated = 0;size_t TotalFreed = 0;
};// 用于记录单个内存分配的信息
struct Allocation
{void* Memory = 0;size_t Size = 0;const char* Category = 0;	// 描述信息,比如记录申请内存分配的代码的位置,该内存的用处等等
};// 对外接口,用于获取记录分配情况的静态对象(仅Memory.cpp可见)
namespace Memory
{const AllocationStats& GetAllocationStats();
}// Map Allocator 自定义的内存分配器,用于管理std::map的键值对的内存分配,
template <class T>
struct Mallocator
{typedef T value_type;Mallocator() = default;template <class U> constexpr Mallocator(const Mallocator <U>&) noexcept {}T* allocate(std::size_t n){
#undef max// 64位操作系统最大寻址内存值为2^64,因此要保证传入的n是小于这个的if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))throw std::bad_array_new_length();if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) {return p;}throw std::bad_alloc();}void deallocate(T* p, std::size_t n) noexcept {std::free(p);}
};struct AllocatorData
{// 2个自定义分配器,分别用于管理std::map中的 这种键值对的内存分配: // key:                   value:// const void* const  --  Allocation// const char* const  --  AllocationStatsusing MapAlloc = Mallocator<std::pair<const void* const, Allocation>>;using StatsMapAlloc = Mallocator<std::pair<const char* const, AllocationStats>>;using AllocationStatsMap = std::map<const char*, AllocationStats, std::less<const char*>, StatsMapAlloc>;// 两个std::map容器// key:内存地址;value: Allocation结构体,记录了内存的指针、大小、描述信息std::map<const void*, Allocation, std::less<const void*>, MapAlloc> m_AllocationMap;// key:描述信息;value: 内存总共分配数量、释放数量AllocationStatsMap m_AllocationStatsMap;std::mutex m_Mutex, m_StatsMutex;
};// 内存分配器接口定义
class Allocator
{
public:static void Init();static void* AllocateRaw(size_t size);static void* Allocate(size_t size);static void* Allocate(size_t size, const char* desc);static void* Allocate(size_t size, const char* file, int line);static void Free(void* memory);static const AllocatorData::AllocationStatsMap& GetAllocationStats() { return s_Data->m_AllocationStatsMap; }
private:inline static AllocatorData* s_Data = nullptr;
};
#include "Memory.h"#include <memory>
#include <map>
#include <mutex>#include "Log.h"
// 用于记录全局内存分配、释放的信息
static Hazel::AllocationStats s_GlobalStats;// 分配器是否正在进行初始化操作(应付多线程)
static bool s_InInit = false;// 初始化阶段,主要是分配一个静态的AllocatorData对象(lazy 初始化)
void Allocator::Init()
{if (s_Data)return;s_InInit = true;AllocatorData* data = (AllocatorData*)Allocator::AllocateRaw(sizeof(AllocatorData));new(data) AllocatorData();	// 定位new(placement new)在指定地址构造目标对象,并调用构造函数初始化,释放需要调用operator deletes_Data = data;s_InInit = false;
}// 利用malloc进行原始内存分配(即不会调用构造和析构),记得手动调用Allocator::free
void* Allocator::AllocateRaw(size_t size)
{return malloc(size);
}void* Allocator::Allocate(size_t size)
{// 如果一个线程正在执行Init()函数,分配请求用原始内存分配来处理if (s_InInit)return AllocateRaw(size);if (!s_Data)Init();void* memory = malloc(size);{std::scoped_lock<std::mutex> lock(s_Data->m_Mutex);Allocation& alloc = s_Data->m_AllocationMap[memory];	// 没有该key就创建,有就返回alloc.Memory = memory;alloc.Size = size;s_GlobalStats.TotalAllocated += size;}return memory;
}// 分配带有描述信息的内存,这个内存不仅要记录到总分配内存计数器中,还要把这种类型的内存单独进行计数
void* Allocator::Allocate(size_t size, const char* desc)
{if (!s_Data)Init();void* memory = malloc(size);{std::scoped_lock<std::mutex> lock(s_Data->m_Mutex);Allocation& alloc = s_Data->m_AllocationMap[memory];alloc.Memory = memory;alloc.Size = size;alloc.Category = desc;s_GlobalStats.TotalAllocated += size;if (desc)s_Data->m_AllocationStatsMap[desc].TotalAllocated += size; // 单独计数}return memory;
}
// line没用到,目前只想逐源文件记录内存分配量
void* Allocator::Allocate(size_t size, const char* file, int line)
{if (!s_Data)Init();void* memory = malloc(size);{std::scoped_lock<std::mutex> lock(s_Data->m_Mutex);Allocation& alloc = s_Data->m_AllocationMap[memory];alloc.Memory = memory;alloc.Size = size;alloc.Category = file;s_GlobalStats.TotalAllocated += size;s_Data->m_AllocationStatsMap[file].TotalAllocated += size;}return memory;
}void Allocator::Free(void* memory)
{if (memory == nullptr)return;{// map中有,计数更新并移除bool found = false;{std::scoped_lock<std::mutex> lock(s_Data->m_Mutex);auto allocMapIt = s_Data->m_AllocationMap.find(memory);found = allocMapIt != s_Data->m_AllocationMap.end();if (found)	{const Allocation& alloc = allocMapIt->second;s_GlobalStats.TotalFreed += alloc.Size;if (alloc.Category)s_Data->m_AllocationStatsMap[alloc.Category].TotalFreed += alloc.Size;s_Data->m_AllocationMap.erase(memory);}}if (!found)LOG("Memory", "Memory block {0} not present in alloc map", memory);}free(memory);
}namespace Memory {const AllocationStats& GetAllocationStats() { return s_GlobalStats; }
}

这篇关于C++多线程环境中进行内存分配跟踪的接口类设计(全局重载new/delete操作符)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/636752

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

python常见环境管理工具超全解析

《python常见环境管理工具超全解析》在Python开发中,管理多个项目及其依赖项通常是一个挑战,下面:本文主要介绍python常见环境管理工具的相关资料,文中通过代码介绍的非常详细,需要的朋友... 目录1. conda2. pip3. uvuv 工具自动创建和管理环境的特点4. setup.py5.

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

MyBatis设计SQL返回布尔值(Boolean)的常见方法

《MyBatis设计SQL返回布尔值(Boolean)的常见方法》这篇文章主要为大家详细介绍了MyBatis设计SQL返回布尔值(Boolean)的几种常见方法,文中的示例代码讲解详细,感兴趣的小伙伴... 目录方案一:使用COUNT查询存在性(推荐)方案二:条件表达式直接返回布尔方案三:存在性检查(EXI