51

我正在寻找一种方法来跟踪 C++ 程序中的内存分配。我对内存泄漏感兴趣,这似乎是大多数工具试图找到的,而是为应用程序创建内存使用配置文件。理想的输出将是函数名称的大列表加上随时间变化的最大分配字节数,或者更好的是,随时间变化的堆的图形表示。横轴是时间,纵轴是堆空间。每个函数都会根据分配的堆字节获得自己的颜色并绘制线条。用于识别分配的对象类型的奖励积分也是如此。

这个想法是找到内存瓶颈/可视化哪些函数/线程消耗最多的内存,应该针对进一步优化。

我简要地查看了 Purify、BoundsChecker 和 AQTime,但它们似乎不是我所追求的。Valgrind 看起来很合适,但是,我在 Windows 上。Memtrack看起来很有希望,但需要对源代码进行重大更改。

我的谷歌技能一定让我失望了,因为这似乎不是一个不常见的请求?创建这样一个工具所需的所有信息都应该可以从程序的调试符号和运行时 API 调用中轻松获得——不是吗?

4

8 回答 8

35

使用 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 手册

您可以跟踪堆分配以及堆栈分配(默认关闭)。

PS。我刚刚读到你在 Windows 上。不过,我会留下答案,因为它给出了您可以从可能的工具中获得什么的图片。

于 2009-05-26T11:55:02.057 回答
21

Microsoft 有据可查的内存跟踪功能。但是,由于某种原因,它们在开发者社区中并不是很有名。这些是 CRT 调试功能。良好的起点将是CRT 调试堆函数

检查以下链接以获取更多详细信息

  1. 堆状态报告函数
  2. 跟踪堆分配请求。可能这就是您正在寻找的功能。
于 2009-05-26T11:35:40.397 回答
18

对于通用 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
于 2009-05-26T16:04:29.963 回答
5

更新: @Skizz 的回答

从 C++20 开始,我们可以使用std::source_location代替宏__FILE____LINE__.

(由于这是一个重大的简化,我相信它值得单独回答)。

于 2020-06-28T22:40:59.343 回答
1

在 Xcode 上,您可以使用 Instruments 来跟踪分配、VM 使用情况和其他几个参数。最受 iOS 开发人员欢迎,但值得一试。

于 2015-10-22T02:37:35.503 回答
0

在 Mac OS X 上,您可以使用代码分析工具 Shark 来执行此操作,IIRC。

于 2009-05-26T15:41:26.673 回答
0

Visual Studio IDE 具有内置的堆分析支持(自 2015 年以来),这可能是最容易开始的。它具有随时间变化的堆使用情况的图形视图,并且可以按函数/方法跟踪分配。

堆分析

CRT 还具有调试和配置文件支持,更详细和更底层。您可以使用其他工具跟踪数据并绘制结果:

特别是看_CrtMemCheckpoint和相关的功能。

于 2019-07-26T14:13:06.130 回答
0

“堆随时间变化的图形表示” - 接近您正在寻找的内容是在Intel(R) Single Event API中实现的,可以在本文中找到详细信息(放在这里相当大)。随着时间的推移内存块分配

它向您显示每个块大小分配的时间线,并允许向您的代码添加额外的标记以更好地理解整个画面。

于 2016-01-05T11:39:46.090 回答