【问题标题】:Making sense of Microsoft's Memory Leak Detection output理解微软的内存泄漏检测输出
【发布时间】:2017-01-31 18:48:06
【问题描述】:

抱歉标题不好,但我只是好奇究竟是什么内存泄漏,我真的可以说我在我的程序中做了一些坏事。

我在 Visual Studio (_CrtDumpMemoryLeaks()) 中运行了内存泄漏测试,它会输出大量检测到的泄漏列表(仅显示一小部分):

{1640} normal block at 0x081C2AB0, 4 bytes long.
 Data: <;   > 3B 00 00 00 
{789} normal block at 0x08B792E8, 12 bytes long.
 Data: <            > 00 00 00 00 00 00 00 00 00 00 00 00 
{788} normal block at 0x027E62D8, 32 bytes long.
 Data: <         L   L  > 20 A8 FE 07 A0 A0 17 08 E4 4C F3 07 D8 4C F3 07 
{787} normal block at 0x027E6328, 32 bytes long.
 Data: <layer03 - Object> 6C 61 79 65 72 30 33 20 2D 20 4F 62 6A 65 63 74 
{786} normal block at 0x08B79748, 8 bytes long.
 Data: <`N      > 60 4E F3 07 00 00 00 00

我真的不知道我到底做错了什么。我遵循了来自 another question of mine 的提示,并用 智能指针 替换了所有 new/delete 的东西(或者至少我认为我这样做了)。

我问的原因是,因为我在运行程序时遇到了内存大小和堆大小的增加,我不确定这是一个严重的问题还是只是某种基本行为。

以我的(自己写的)gui为例:

std::shared_ptr<Gui_Button> gui_modal_window_map_saved_button;

我现在称它为:

gui_modal_window_map_saved_button.reset(new Gui_Button(res_handler, "Select", 20, 345, 70, 40, BUTTON_CONFIRM));

已被告知是内存安全的。现在,有些事情我不太确定——我现在如何安全地删除按钮?我知道,没有什么能像使用智能指针删除一样,但我想在不再需要按钮时删除按钮,即在相应的窗口关闭之后。

目前我正在使用reset()“删除”我的按钮

if (g_ev.event_id == EVENT_CLICKED && g_ev.element == gui_modal_window_map_saved_button.get()) {
        gui_modal_window_map_saved.reset();
        gui_modal_window_map_saved_button.reset();
    }

我认为这没问题,但为什么每次创建“新”按钮时我的内存大小都会增加(而不是再次减少)?

回到原来的问题

我通过 Visual Studio 获得了这些内存泄漏转储,但我真的不知道如何使用这些信息。究竟是什么导致了内存泄漏?我现在只使用“普通”指针,我需要将某些东西传递给另一个类,即用智能指针替换所有new/deletes。我真的无法显示我的代码,因为到目前为止它大约有 3000 行..

那么,究竟是什么导致了内存泄漏以及如何使用 VS 提供的信息呢?

【问题讨论】:

  • 这是一个一般性问题?
  • 内存泄漏与硬件无关,除非您将笔记本电脑放在雨中...
  • 灰色区域。我想说内存泄漏是一个普遍的问题。
  • “我现在只使用“普通”指针,我需要将某些东西传递给另一个类” - 听起来你应该改用引用。

标签: c++ visual-studio memory-leaks


【解决方案1】:

Microsoft 提供了有关如何利用您所看到的数据的详细信息:https://msdn.microsoft.com/en-us/library/x98tx3cf.aspx

可以在分配时使用分配号来中断您的代码。在调试输出的以下行中:

{789} normal block at 0x08B792E8, 12 bytes long.

789 是分配号。您提到内存使用量正在增长。这并不一定说明泄漏越来越多,除非当您进行更长的会话时泄漏消息相应增加。

要考虑的另一件事是泄漏发生在您的班级中。如果您的对象中分配的任何元素未正确处理,则没有智能指针可以拯救您。

【讨论】:

  • “如果您在运行结束时没有看到泄漏记录增加,则说明您的泄漏没有增加。” - 你能再解释一下吗?在运行结束时我将如何衡量记录?
  • 数一数。如果数量超过您可以快速计算的数量,那么它是否在增长并不重要。你的问题已经够多了。
【解决方案2】:

C++ 与许多较新的语言的不同之处在于它没有所谓的垃圾收集器。您使用new 分配(声明)内存并使用delete 再次显式释放(释放)它。

如果您忘记了delete,则只有在您的程序存在后,操作系统才会释放内存。

为了平衡newdelete 的使用,如果可能,仅在构造函数中分配内存并在(虚拟)析构函数中释放内存。这样,如果拥有一块堆内存(使用new分配的内存)的对象超出范围(例如离开函数时的局部变量),内存将自动释放。

顺便说一句,以这种方式分配和释放内存是赋予 C++ 多功能性和速度的原因之一。所以这是一种资产,而不是需要避免的东西。但需要小心使用。

【讨论】:

  • 是的,这是我学到的。但是,如果我没有明确地调用 new,那么我应该不会遇到任何问题,不是吗?
  • 确实,你不应该。
  • 在其他一些答案中,我读到我有可能调用 _CrtDumpMemoryLeaks();在不合适的地方。目前,我把它放在程序结束之前(在返回 0 之前;)
  • 也许您可以通过这种方式诊断问题,但如果您的设计需要它,那么您就有问题了。最好坚持构造函数/析构函数规则,通常被表述为“构造是资源分配”,“破坏是资源释放”。顺便说一下,资源也可以是打开的文件、信号量之类的东西。
  • @JacquesdeHooge:通常情况下,这很糟糕,因为标准库容器和std::unique_ptr 对于绝大多数用例来说已经足够了,但也有一些例外情况,比如事件中的对象-处理系统想要在析构函数中删除自己。观察原始指针是另一个例外。
猜你喜欢
  • 2010-12-18
  • 2015-04-16
  • 2012-07-16
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 2012-01-22
相关资源
最近更新 更多