通过全局重载
// 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 -- AllocationStats using 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 delete s_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.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; } }