_CrtMemDumpAllObjectsSince不返回预期结果
_CrtMemDumpAllObjectsSince not returning expected results
我正在使用_CrtMemCheckpoint和_CrtMemDumpAllObjectsSince来跟踪dll中可能的内存泄漏。
在DllMain中,当检测到DLL_PROCESS_ATTACH时,将调用一个init函数,该函数在全局_CrtMemState变量startState上调用_CrtMemCheckpoint(&startState)。 当检测到DLL_PROCESS_DETACH时,将调用一个调用_CrtMemDumpAllObjectsSince(&startState)的退出函数。 这返回
ExitInstance()Dumping objects ->
{8706} normal block at 0x07088200, 8 bytes long.
Data: <p v > 70 FF 76 07 01 01 CD CD
{8705} normal block at 0x07084D28, 40 bytes long.
Data: < > 00 00 00 10 FF FF FF FF FF FF FF FF 00 00 00 00
{4577} normal block at 0x070845F0, 40 bytes long.
Data: <dbV > 64 62 56 0F 01 00 00 00 FF FF FF FF FF FF FF FF
{166} normal block at 0x028DD4B8, 40 bytes long.
Data: <dbV > 64 62 56 0F 01 00 00 00 FF FF FF FF FF FF FF FF
{87} normal block at 0x02889BA8, 12 bytes long.
Data: < P > DC 50 90 02 00 00 00 00 01 00 00 00
到目前为止一切顺利,除了最后三个条目(4577、166 和 87)也在 startState 中。 即 如果我在我的 Init 函数和 Exit 函数中运行 _CrtDumpMemoryLeaks(),这些条目都在两个列表中。
文档是这样说的:
_CrtMemDumpAllObjectsSince 使用 state 参数的值来确定在何处启动转储操作。开始倾销从 指定的堆状态,状态参数必须是指向 _CrtMemState在调用_CrtMemDumpAllObjectsSince之前由_CrtMemCheckpoint填充的结构。
这让我相信在 startState 中跟踪的项目将被排除在输出之外。 在调用 _CrtMemCheckpoint 的 Init 函数结束时,大约有 4700 次分配调用。 _CrtMemDumpAllObjectsSince不应该只转储在该检查点调用之后分配的对象吗?
我错过了什么?
短
这显然很奇怪,但它(部分)完成了工作,但是以一种过分热心的模式
。这些功能已有几十年的历史,所以不是错误,但设计得并不完全好。
事实是,在"旧"状态中,有一些东西会在你的"自"状态之后发生变化。
所以问题是"是的,它确实反映了此后的变化,但它是致命的泄漏吗?">
这种频繁和放大了 DLL 的延迟初始化。
此外,还有许多复杂的对象,如map/string/array/list,它确实延迟了内部缓冲区的分配。
坏消息是,几乎所有声明为"静态"的复杂对象实际上都是在第一次使用时启动的。
因此,这些变化应该以_CrtMemDumpAllObjectsSince
显示,因为它们改变了它们的记忆分配。
不幸的是,显示是如此粗糙和未经过滤,以至于它也显示了太多不相关的块(未修改)。
典型的最大罪魁祸首是使用"realloc
"来改变旧alloc
的状态
这甚至可能看起来更奇怪,因为它们可能会消失,
例如,在统计快照后进行真正的malloc
时,因为这会对用于转储的低水位标记进行一种"重置",将其设置为更高的水平。这神奇地让你的一堆"额外显示器"消失了。
如果您正在执行多线程,则行为会更加不稳定,因为它很容易变得不重复。
注意:
它不显示文件名和行号的事实表明它在这里处理的是预初始化。
所以罪魁祸首很可能是在main()
(或InitInstance();
)之前启动的静态复杂对象
龙:
_CrtMemDumpAllObjectsSince
痛苦!
事实上,可能如此混乱的无用信息,以至于它违背了每天/每天简单使用_CrtMemDumpAllObjectsSince
的目的。 (在它激发的良好本质中)
解决方法
不简单!
您可以尝试做一个malloc,然后在你做你的"自"状态快照后释放,以取笑这个标记。
但为了更安全、更易于控制,不幸的是,我确实看到了从原始 MS 结构中转储的自己的_MyCrtMemDumpAllObjectsSince
的方法。 这是受到static void __cdecl dump_all_object_since_nolock(_CrtMemState const* const state) throw()
(见"debug_heap.cpp")版权Microsoft的启发!
代码"按原样"可用,更多是为了获得灵感。
但在对_CrtMemState
工作方式进行一些解释之前:_CrtMemState
状态确实有一个指针"pBlockHeader
",它通常是"_CrtMemBlockHeader*
"的双链表的链接 ">
这个列表实际上不仅仅是一个快照,而是对所有正在使用的内存块的选择(不清楚如何), 以这样的方式排列,即"当前状态"直接指向"pBlockHeader
">
,因此去
->_block_header_prev
允许探索旧块
->_block_header_next
允许探索新块(您在概念"自"中寻找的果汁,但非常危险,因为没有结束标记)
棘手的部分:
MS维护一个重要的内部静态_CrtMemBlockHeader*
,称为__acrt_first_block
此__acrt_first_block
在分配和重新分配期间不断变化
但是,_MyCrtMemDumpAllObjectsSince
转储确实从此__acrt_first_block
开始并向前(使用_block_header_next
),直到找到 NULL ptr
处理的第一个块由这个__acrt_first_block
决定,并且您发送的"状态"不超过转储的 STOP。
否则说_CrtMemDumpAllObjectsSince
并没有真正转储"自"状态,
而是从__acrt_first_block
转储到您的"自"状态。
for"循环通过显示从"开始"(自)到"结束"(自以来最早修改)的块而矫枉过正。
这是有道理的,但这也包括尚未修改的转储块。展示我们不关心的事情。
MS结构很聪明,可以直接使用,但不能保证Microsoft将来_CrtMemBlockHeader
保持相同的重要结构
但在过去的15年里,我没有看到它有任何变化(我也没有预见到他们会改变战略和关键结构的任何原因)。
我不喜欢复制/粘贴 MS 代码并使用我的背负代码
解析链接器 所以我使用的解决方法是基于拦截发送到"输出"窗口的文本消息的能力,解码并将所有信息存储在我自己的银行
中
下面的结构给出了使用锁定下的静态结构来存储所有信息的
截距的想法
_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL,MyReportHookDumpFilter);
_CrtMemDumpAllObjectsSince(state); // CONTRARY to what it says, this thing seems to dump everything until old_state
_CrtSetReportHook2(_CRT_RPTHOOK_REMOVE,MyReportHookDumpFilter);
_MyReportHookDumpFilterCommand(_CUST_SORT,NULL);
_MyReportHookDumpFilterCommand(_CUST_DUMP,NULL);
"_MyReportHookDumpFilterCommand"确实会检查根本不被修改的块的预先存在,并避免在转储阶段显示这些块 将其
作为代码的灵感以简化显示。如果有人有更简单的使用方法,请分享!
- 如何返回一个类的两个对象相加的结果
- 如果我std::dynamic_pointer_cast并且底层dynamic_cast的结果为null,那么返回的sh
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- C++Brute Force攻击函数不会返回结果
- 如何从递归函数中完全返回,该函数给出了每个函数结果的累积相加?
- 如何读取 C++ SAFEARRAY**,该 SAFEARRAY** 是 COM 互操作的结果,其中 C# 返回值为
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- c++ lambda:柯里和函数:使用按值捕获与按引用捕获返回不同的结果
- C++ 获取函数在常量引用中按值返回的结果
- 如何在一个表达式中生成并返回结果?
- 从返回 std::optional of std::vector 的函数中获取结果到调用方
- 三元运算符在返回语句中给出意外的结果
- 为什么函数 tellg() 没有返回好的结果?
- 为什么我可以改变常量对象中的成员变量,这是返回常量对象函数的结果?
- 有没有办法将 for 循环结果返回到像三元运算符这样的函数中?
- 使用std::tie进行类似golang的错误处理,同时返回结果,是否有缺点?(C++11)
- 检查文本文件中的 0 或 1,并根据结果返回
- 如何使用boost::any_range连接多个boost范围并作为函数w/o的结果返回
- 如何从线程的结果返回Qt对象(Qtfutur)
- C++:当在函数内部创建新对象并将其作为结果返回时,我必须使用新运算符来创建对象