FastMM 报告 C++ Builder 6 中 STL 容器的内存泄漏

Posted

技术标签:

【中文标题】FastMM 报告 C++ Builder 6 中 STL 容器的内存泄漏【英文标题】:FastMM reports memory leaks on STL containers in C++ Builder 6 【发布时间】:2016-07-05 11:15:08 【问题描述】:

当我创建一个空的控制台应用程序并在其中使用 STL 容器时,FastMM 在应用程序关闭时报告内存泄漏。

例如,如果我在main() 中创建std::vector<int>

std::vector<int> v;

编译,运行,关闭,不报告泄漏。

如果我这样做:

std::vector<int> v;
v.push_back(100);

我明白了:

此应用程序已泄漏内存。小块泄漏是:

309 - 340 字节:未知 x 1

同样,我收到了关于以下内容的泄漏报告:

std::vector<int> v;
v.push_back(100);
v.clear();

还报告了泄漏:

std::vector<int> v;
v.reserve(1);

对于某些容器,例如std::deque,只需创建一个容器就足够了,即使不更改其内容,应用程序关闭时也会报告泄漏。

谁能解释发生了什么?我使用 Borland C++Builder 6 和 FastMM4。我一直在更改 FastMMOptions.inc 中的各种设置,但我仍然看到报告了这些泄漏。

【问题讨论】:

Borland C++Builder 6 似乎是在 2002 年问世的......你有机会使用更现代的编译器吗?甚至是更新版本的 C++Builder?对于软件而言,14 年是一段很长的时间。 我说,因为给定代码的 sn-p,除了编译器错误的 std::vector 实现之外,没有什么会导致内存泄漏(没有看到周围的上下文)。或检漏仪报告的误报。 是的,我们有 Borland XE7,新项目是在新环境中开发的。但是我需要调查现有项目中的内存泄漏,由于它们的大小,我们还没有迁移到 XE7。我发现 FastMM 非常有用,但是因为在我们的代码中很多地方都使用了 STL,所以它指出了让我非常困惑的漏洞。 @CoryKramer:有时人们无法选择使用现代编译器。我自己在日常工作中仍然使用 C++Builder 6。不是因为我想要,而是因为我的公司需要它(不是因为多年来没有尝试升级,PTB 不允许它,因为新版本更不稳定并且不能满足业务需求)。 std::deque 在 C++Builder 6 中被窃听且无法使用 【参考方案1】:

清除std::vector 不会释放用于vector 内部数组的内存,它只是破坏数组中的项目,然后将std::vector::size() 设置为0。数组本身仍然是分配的,所以它可以重复用于vector 中推送的新项目。 vector 的析构函数将释放数组。

在 C++Builder 6 中,默认的 STL 库是 STLPort(在 C++Builder 2006 中被 Dinkumware 取代)。 STLPort 对~std::vector() 的实现仅破坏了数组项(好像clear() 已被调用),但不会释放数组本身,因此您看到的是“泄漏”。根据 STLPort 网站上的以下常见问题解答,这实际上根本不是泄漏:

Q6.2 My tool detect memory leaks in application with STLport. Is this leak from STLport?

A6.2 在大多数情况下,这是某些工具错误支持的“伪内存泄漏”。

在STLport的默认编译中,节点分配器用于分配内部内存。节点分配器通过预先分配一大块内存并分发小内存块来工作。在运行使用 STLport 的应用程序期间,内存块没有被释放(即它没有返回到系统,有像 BoundsChecker、Purify 或 Valgrind 这样的工具来检查 memorytml 泄漏,对于在不再使用。当使用 STLport 的节点分配器时,这些工具可能会报告错误的内存泄漏。内存块通常在应用程序端释放,但内存检查器通常会在此之前报告内存泄漏。另一个内存问题当您使用内部使用 STLport 并静态链接到它的共享库(例如 DLL,此问题特定于 Windows DLL 模型)时可能会报告。如果内存在 dll 中分配并在另一个 dll 中释放,则 STLport 节点分配器将保留释放的内存以备将来使用。如果您不使用此内存,那么即使没有真正的内存泄漏,您的应用程序全局内存消耗也会增长直到应用程序崩溃。这就是为什么您应该始终使用连贯的配置 dll 中的所有内容或静态库中的所有内容。

有一些方法可以消除伪内存泄漏(由于内存在程序结束时被正确释放,泄漏只是伪内存)。您可以使用 STLport 中使用的另一个分配器。打开文件"stlport/stl/_site_config.h" 并取消注释以下任一选项:

_STLP_USE_NEWALLOC   enables a simple allocator that uses "new/delete"
_STLP_USE_MALLOC     enables a simple allocator that uses "malloc/free"

new/delete 分配器具有提供跟踪内存不足的入口点的优势,有关详细信息,请参阅编译器文档中的 set_new_handler 或 C++ 标准。

您也可以定义以下符号,只需在 "stlport/stl/_site_config.h" 中取消注释即可。

_STLP_LEAKS_PEDANTIC

该符号强制释放所有内存块。另请参阅配置文件中符号周围的 cmets。

请注意,如果您对文件进行任何更改,您必须重新编译 STLport 以及您的应用程序和所有依赖库!

还有一些定义有助于调试 STLport 中的内存问题。在 _STLP_DEBUG 模式下,只需在“./stlport/stl_user_config.h”或您的项目设置中定义以下符号:

_STLP_DEBUG_ALLOC
_STLP_DEBUG_UNINITIALIZED        

您不需要为这些选项重新构建 STLport,但如果您对文件进行任何更改,则必须重新构建您的应用程序和所有依赖库。

也就是说,在早期 C++Builder 版本中使用的 RogueWave STL 也随 C++Builder 6 一起提供,以向后兼容旧代码,并且不会遇到此问题。您可以通过在项目的条件列表中定义 _USE_OLD_RW_STL 从 STLPort 切换到 RogueWave。

【讨论】:

优秀且内容丰富的回复。定义 _USE_OLD_RW_STL 后,FastMM 不会报告我们项目中使用的容器的任何泄漏(std::vectorstd::liststd::deque)。非常感谢你。现在查看内存泄漏报告要容易得多。 我注意到的另一件事是,std::cout 的任何使用都会报告泄漏。幸运的是,我们的代码中没有很多,但如果你知道另一个“神奇的条件定义”来阻止那些出现在报告中,那就太好了。示例:std::cout &lt;&lt; "hello" &lt;&lt; endl; 报告:13 - 20 字节:未知 x 1 21 - 36 字节:std::codecvt x 1,未知 x 2 37 - 52 字节:未知 x 2 53 - 68 字节:std::ctype x 1,__rwstd::locale_imp x 2 85 - 100 字节:未知 x 2 485 - 532 字节:未知 x 3,而 printf("hello\n"); 报告没有泄漏

以上是关于FastMM 报告 C++ Builder 6 中 STL 容器的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

delphi fastmm4 调试

Delphi7 中使用FastMM 转载

Borland C++ builder 6 链接器错误

虚拟内存地址空间不足(Borland C++ Builder 6 程序)

Borland Builder C++ 6 中的 CoreAudio API

Borland C++ Builder 6 上的奇怪链接器错误