现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34199 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

35 | 发现和识别内存问题:内存调试实践

malloc_allocator 实现
独立分配器避免干扰
全局 RAII 对象控制
报告内存泄漏
遍历内存块链表
摘除链表结点和释放内存
验证地址有效性
倒推链表结点地址
alloc_memfree_mem 函数
转发内存分配和释放请求
布置分配和释放函数
宏简化调用
RAII 类自动化管理
类栈结构存放上下文
文件名、函数名、行号等
定义和生成
分配器
operator newoperator delete
dequestack
RAII
确认内存释放合法性
未释放内存检查
模块和线程内存占用检查
性能影响
特殊内存分配器不兼容
[3] 吴咏炜, nvwa
[2] Microsoft, Predefined macros
[1] GNU, GCC Manual
错误检测和输出
RAII 计数对象
对齐分配问题
多线程加锁保护
跨平台和实际使用场景
Nvwa 项目内存调试器
检查内存泄漏
记录上下文信息
栈式上下文内存调试工具
一个小细节
退出检查
释放检查
记录上下文
上下文的产生和销毁
上下文
预备知识
分类统计内存使用
检查匹配情况
记录内存分配信息
工具使用和自定义代码调试
现有工具的局限性
参考资料
课后思考
内容小结
“上下文”内存调试工具
内存调试原理
场景
内存调试实践

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
作为内存相关话题的最后一讲,今天我们来聊一聊内存调试的问题。

场景

首先,目前已经存在一些工具,可以让你在自己不写任何代码的情况下,帮助你来进行内存调试。用好你所在平台的现有工具,已经可以帮你解决很多内存相关问题(在第 21 讲中我已经介绍了一些)。
不过,前面提到的工具,主要帮你解决的问题是内存泄漏,部分可以帮你解决内存踩踏问题。它们不能帮你解决内存相关的所有问题,比如:
内存检测工具可能需要使用自己的特殊内存分配器,因此不能和你的特殊内存分配器协作(不使用标准的 malloc/free
某些内存调试工具对性能影响太大,无法在实际场景中测试
你需要检查程序各个模块的分别内存占用情况
你需要检查程序各个线程的分别内存占用情况
……
总的来说,现成的工具提供了一定的功能,如果它直接能满足你的需求,那当然最好。但如果你有超出它能力的需求,那自己写点代码来帮助调试,也不是一件非常困难的事情——尤其在我们了解了这么多关于内存的底层细节之后。

内存调试原理

内存调试的基本原理,就是在内存分配的时候记录跟分配相关的一些基本信息,然后,在后面的某个时间点,可以通过检查记录下来的信息来检查跟之前分配的匹配情况,如:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了内存调试的实践方法和原理,提出了自行编写内存调试工具的建议。作者详细介绍了内存调试的基本原理,包括记录内存分配相关信息、检查内存匹配情况以及根据不同使用场景记录不同信息等。文章还介绍了基于“上下文”的内存调试工具,并给出了一个小小的内存调试工具的实现示例。在实现示例中,作者讨论了在程序退出时进行内存泄漏检查的典型应用,并介绍了一个小细节,即为`context_stack`准备了一个独立的分配器,以避免内存占用被记录到“当前”上下文中。总的来说,本文通过深入浅出的方式介绍了内存调试的实践方法和原理,对读者进行了技术上的指导和启发。文章内容涵盖了内存调试的基本原理、自行编写内存调试工具的建议以及实现示例,对读者进行了全面的技术指导。值得一提的是,文章最后还提供了一个课后思考部分,引导读者进一步探索实际实现中增加的复杂性。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(6)

  • 最新
  • 精选
  • 转遍世界
    你好吴老师,您这个内存调试实践代码主要是针对内存泄漏和重复释放的问题。但工作中基本用的是智能指针,这些问题出现的可能性不大。工作中主要的内存问题是野指针问题,比如数组越界访问,迭代器失效后访问非法内存,主要还是访问非法内存问题居多,请问如果想检查这类内存错误可以用代码去检测吗?还怎么去实现?

    作者回复: 这类问题没什么代码检测的好办法。只能使用外部工具,如 Address Sanitizer 和 Clang-Tidy。目前主要还是靠前者这样的运行期工具。

    2024-01-15归属地:江苏
    1
  • H X
    请问 1 .valgrind的内存检测工具 memcheck 和massif 这两个,能否根据我自己的项目 对其源码进行修改。最小影响的 检测我的项目中内存泄漏问题。2、cpp中没有jvm的GC。我用valgrind memcheck查出很多 still reachable。我理解这是内存碎片(vector和string频繁增删数据导致的)针对still reachable 有好的办法解决吗?多谢老师

    作者回复: Valgrind 使用 GPL v2 发布,显然你可以自己去修改它的源代码。 still unreachable 我怀疑是真正的泄漏而不是内存碎片。你可以换用其他的内存检测工具试一下。C++ 理论上现在应该不容易产生内存泄漏了,如果你不用有所有权的裸指针的话——确实很少再有理由用了。

    2022-05-25
    1
  • Geek_595be5
    请问上面示例中get_current_context函数返回的是引用会不会不安全,毕竟因stack实现不同,可以通过pop、push对之前的引用造成内容擦写

    作者回复: 在目前我的用法下,拿到引用立即调用另一个函数(而不是保存下来),是没有问题了。这里主要关注性能,而不是防误用。

    2023-08-30归属地:新加坡
  • tony
    convert_use_ptr中计算usr_ptr的长度是否在任何情况下都没有问题?比如分配的内存大小正好是对齐的且后续内存地址中的内容也不为0。谢谢老师。 “auto offset = static_cast<byte*>(usr_ptr) - static_cast<byte*>(nullptr);”

    作者回复: 我没看懂你的问题。分配的内存大小是强制对齐的,也跟“后续内存地址中的内容”是不是0完全没有关系……

    2023-03-30归属地:安徽
  • awmthink
    吴老师,请问:memory_trace里 “使用 RAII 计数对象来尽可能延迟对 check_leaks 的调用” 怎么理解呢?我看代码时,是把check_leaks调用放在了一个static对象的析构里,和全局对象invoke_check_leak相比,行为有什么不同吗?(防止有其他编译模块定义的全局对象生命周期比invoke_check_leak更长吗?)

    作者回复: 不同翻译单元里的全局/静态对象的析构顺序没有保证。文中的技巧可以保证析构发生在所有包含了这个头文件的源文件里的全局/静态对象的析构之后。

    2023-02-15归属地:广东
  • ericaaa
    老师您好,我对free_mem最后一行free(ptr)有一些不大理解。这里的ptr类型是alloc_list_t*,这样是否只释放了head所占的memory而没有释放user object所占的memory呢?

    作者回复: free 的参数类型是 void*,非强类型。它不管你内存是怎么使用的:只要你给 free 的是 malloc 得到的指针并之前没有释放过,操作就应该成功——把整个 malloc 分配的内存块释放掉。

    2022-05-27
收起评论
显示
设置
留言
6
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部