跟踪 C++ 内存分配
Posted
技术标签:
【中文标题】跟踪 C++ 内存分配【英文标题】:track C++ memory allocations 【发布时间】:2010-10-28 22:51:25 【问题描述】:我正在寻找一种方法来跟踪 C++ 程序中的内存分配。我不对内存泄漏感兴趣,这似乎是大多数工具试图找到的,而是为应用程序创建内存使用配置文件。理想的输出将是函数名称的大列表加上随时间变化的最大分配字节数,或者更好的是,随时间变化的堆的图形表示。横轴是时间,纵轴是堆空间。每个函数都会根据分配的堆字节获得自己的颜色并绘制线条。识别分配的对象类型的奖励积分也是如此。
这个想法是找到内存瓶颈/可视化哪些函数/线程消耗的内存最多,应该针对进一步优化。
我简要地查看了 Purify、BoundsChecker 和 AQTime,但它们似乎不是我所追求的。 Valgrind 看起来很合适,但是,我在 Windows 上。 Memtrack 看起来很有希望,但需要对源代码进行重大更改。
我的谷歌技能一定让我失望了,因为这似乎不是一个不常见的请求?创建此类工具所需的所有信息都应该可以从程序的调试符号和运行时 API 调用中轻松获得 - 不是吗?
【问题讨论】:
【参考方案1】:更新:@Skizz 的回答
从 C++20 开始,我们可以使用 std::source_location 代替 __FILE__
和 __LINE__
等宏。
(由于这是一个重大的简化,我认为它值得单独回答)。
【讨论】:
【参考方案2】:Visual Studio IDE 具有内置的堆分析支持(自 2015 年起),这可能是最容易开始的。它具有随时间变化的堆使用情况的图形视图,并且可以按函数/方法跟踪分配。
Measure memory usage in Visual StudioCRT 还具有调试和配置文件支持,更详细和更底层。您可以使用其他工具跟踪数据并绘制结果:
CRT Debug Heap Details特别看_CrtMemCheckpoint
及相关函数。
【讨论】:
【参考方案3】:Microsoft 有详细记录的内存跟踪功能。但是,由于某种原因,它们在开发人员社区中并不是很有名。这些是 CRT 调试功能。好的起点是CRT Debug Heap functions。
查看以下链接了解更多详情
-
Heap state reporting functions
Tracking heap allocation requests。可能这就是您正在寻找的功能。
【讨论】:
是的,可能 _CrtMemDumpAllObjectsSince 就是他要找的所有东西。【参考方案4】:“堆随时间的图形表示” - 接近您正在寻找的内容在Intel(R) Single Event API 中实现,详细信息可以在this article 中找到(放在这里相当大)。
它向您显示每个块大小分配的时间线,并允许在您的代码中添加额外的标记以更好地了解整个情况。
【讨论】:
【参考方案5】:在 Xcode 上,您可以使用 Instruments 来跟踪分配、VM 使用情况和其他几个参数。最受 ios 开发者欢迎,但值得一试。
【讨论】:
【参考方案6】:对于通用 C++ 内存跟踪器,您需要重载以下内容:
global operator new
global operator new []
global operator delete
global operator delete []
any class allocators
any in-place allocators
棘手的一点是获取有用的信息,重载的运算符只有分配器的大小信息和删除的内存指针。一种答案是使用宏。我知道。可恶的。一个示例 - 放置在包含在所有源文件中的标头中:
#undef new
void *operator new (size_t size, char *file, int line, char *function);
// other operators
#define new new (__FILE__, __LINE__, __FUNCTION__)
并创建一个源文件:
void *operator new (size_t size, char *file, int line, char *function)
// add tracking code here...
return malloc (size);
只有在类范围内没有定义任何 operator new 时,上述方法才有效。如果您确实有一些在课堂范围内,请执行以下操作:
#define NEW new (__FILE__, __LINE__, __FUNCTION__)
并将“新类型”替换为“新类型”,但这可能需要更改大量代码。
因为它是一个宏,删除内存跟踪器非常简单,标题变为:
#if defined ENABLED_MEMORY_TRACKER
#undef new
void *operator new (size_t size, char *file, int line, char *function);
// other operators
#define NEW new (__FILE__, __LINE__, __FUNCTION__)
#else
#define NEW new
#endif
以及实现文件:
#if defined ENABLED_MEMORY_TRACKER
void *operator new (size_t size, char *file, int line, char *function)
// add tracking code here...
return malloc (size);
endif
【讨论】:
我需要在 CygWin 和 Win 7 上更改#define NEW new ((char*)__FILE__, __LINE__, (char*)__FUNCTION__)
,正如在 CodeReview 上向我指出的那样,size_t
在多个标题中定义,因此 std::size_t
应该更多更可取。
在此解决方案中,请确保您的跟踪代码本身不会在堆(包括 STL 容器)上分配内存,否则程序将进入无限循环。【参考方案7】:
使用 Valgrind 及其工具 Massif。它的示例输出(它的一部分):
99.48% (20,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->49.74% (10,000B) 0x804841A: main (example.c:20)
|
->39.79% (8,000B) 0x80483C2: g (example.c:5)
| ->19.90% (4,000B) 0x80483E2: f (example.c:11)
| | ->19.90% (4,000B) 0x8048431: main (example.c:23)
| |
| ->19.90% (4,000B) 0x8048436: main (example.c:25)
|
->09.95% (2,000B) 0x80483DA: f (example.c:10)
->09.95% (2,000B) 0x8048431: main (example.c:23)
所以,你会得到详细的信息:
WHO 分配了内存(上例中的函数:g()、f() 和 main());您还可以获得导致分配功能的完整回溯, 内存去了哪个数据结构(上例中没有数据结构), 当它发生时, 占所有已分配内存的百分比(g:39.7%,f:9.95%,main:49.7%)。这里是Massif manual
您可以跟踪堆分配以及堆栈分配(默认关闭)。
附言。我刚刚读到你在 Windows 上。不过我会留下答案,因为它可以让您了解您可以从可能的工具中获得什么。
【讨论】:
Massif 不支持多核,导致程序速度太慢,有没有其他工具可以支持多核? 还有github.com/KDE/massif-visualizer(但请注意***.com/a/49340854/560648)。示例:cn.opendesktop.org/img//hive/content-pre1/122409-1.png【参考方案8】:在 Mac OS X 上,您可以使用代码分析工具 Shark 来执行此操作,IIRC。
【讨论】:
以上是关于跟踪 C++ 内存分配的主要内容,如果未能解决你的问题,请参考以下文章