编程高手必学的内存知识
海纳
华为编译器高级专家,原 Huawei JDK 团队负责人
20674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
编程高手必学的内存知识
15
15
1.0x
00:00/00:00
登录|注册

09 | 深入理解堆:malloc和内存池是怎么回事?

你好,我是海纳。
第 3 节课,我们讲到线性地址空间按照功能的不同,可以分为不同的区域。同时,我们还简单介绍了,如何使用 sbrk 和 mmap 这两个系统调用,向操作系统申请堆内存。
其实,堆内存是程序员打交道最多的一块区域,无论是哪种编程语言,正确合理并高效地使用堆内存,都是极具挑战的一件事情。对程序调优是系统程序员常见的工作任务,而堆内存的管理和分配恰恰是最容易出现性能瓶颈的模块。
不过,sbrk 和 mmap 这两个系统调用分配内存效率比较低,我们在第 5 节课讲过,进程的内核态和用户态的区别,执行系统调用是要进入内核态的,运行态的切换会耗费不少时间。为了解决这个问题,人们倾向于使用系统调用来分配大块内存,然后再把这块内存分割成更小的块,以方便程序员使用,这样可以提升分配的效率。
在 C 语言的运行时库里,这个工作是由 malloc 函数负责的。但有时候 C 语言的原生 malloc 实现还是不能满足特定应用的性能要求,这就需要程序员来实现符合自己应用要求的内存池,以便自己进行内存的分配和释放。
这节课,我们就一起来学习,如何对通过系统调用申请来的大块内存进行更精细化的管理。通过这节课的学习,你将了解到堆内存管理的常用方法,以及内存泄露、double free 等常见的内存问题产生的原因和排查方法,从而提高自己分析和解决内存问题的能力。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了堆内存管理的重要性以及相关的性能优化和常见问题排查方法。从malloc函数的基本功能和实现原理到内存的精细化管理,包括空闲链表的概念和使用链表进行分配和回收的算法,文章详细介绍了堆内存管理的常用方法。其中,分桶式内存管理和伙伴系统被重点讨论。分桶式内存管理采用多个链表,将相同大小的内存区域挂载到同一个链表上,以提高分配内存的效率和碎片控制。而伙伴系统则动态地根据分配请求将大的内存分割成小的内存,并在释放内存时合并相邻的空闲内存,从而提高内存管理的弹性。此外,文章还介绍了malloc的实现策略的灵活性,以及Tcmalloc库在多线程情况下性能提升的改进。最后,文章提到了自己动手实现内存管理库的意义,以及如何更好地解决内存问题并深入理解内存管理的技术细节。通过本文的学习,读者可以快速了解堆内存管理的重要性、常见方法和技术细节,提高自己分析和解决内存问题的能力。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编程高手必学的内存知识》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(13)

  • 最新
  • 精选
  • 大豆
    老师,我有个疑问,通过mmap分配的内存是在进程的映射区还是堆中,还是都有?

    作者回复: 在进程的映射区中。不用太在意这个概念的区分,你要在意的是,mmap出来的内存区域被拿去做什么了。它可以用于malloc分配堆内存,也可以用于协程的栈内存。所以一块内存区域到底是什么,取决于你怎么用他,而不是它自己的地址在哪里。换句话说,根据地址划分的区域不是绝对的,根据它的作用,内存区域的性质也是可以发生变化的。

    2021-11-12
    13
  • qinsi
    看完感觉自己可以实现个valgrind了,不过又觉得少了亿点点细节。于是查了下valgrind的实现,发现只有用到了preload这点是一样的。剩下的部分valgrind相当于实现了一个虚拟机,将机器指令转成虚拟机IR,插桩,再通过JIT生成机器指令执行。这样看来Java字节码增强就是个弟弟阿… 另外llvm因为本身就有IR,所以就可以直接在IR上做,据说asan就是这么实现的。

    作者回复: 厉害,你调查得很深入了。

    2021-11-12
    7
  • LDxy
    老师,为什么很多用C语言做开发的人都说glibc里面的内存分配malloc的性能不行,所以要自己实现一个动态内存管理机制。可我也没看出glibc里面的malloc有啥大问题,自己去重新实现一套内存管理,真的能比标准库的性能提升吗?而且究竟该怎么测试动态内存管理机制的性能,似乎也无从下手。而且,好像就只有C程序员在意动态内存分配的性能问题,其他语言比如C++,Java的程序员就不怎么关心动态内存分配的性能问题。

    作者回复: tcmalloc和jemalloc都比malloc的性能好。c++也很在意这一点的。java是内存全部自己托管了。不存在这个问题。

    2021-11-23
    2
    2
  • 慢动作
    error里也有地址,这额外日志没有给出额外的信息?

    作者回复: 嗯。这里主要演示了一下如何做函数替换,其他的信息,比如当时的调用栈什么的,需要更多调试器的实现原理,所以我们就没再讲了。如果有兴趣可以参考一下gdb如何打印调用栈,valgrind如何追踪泄漏内存。

    2021-11-12
    2
  • 郑童文
    请问老师malloc函数用mmap向系统申请一大块内存的时候,这一大块内存就已经映射到物理内存了吗? 还是在malloc从这一大块内存中分配一小块内存给进程时,才会把这一小块内存映射到物理内存?

    作者回复: 不会mmap,malloc都不会使虚拟内存映射到物理内存。只有你在访问他的时候才会触发缺页中断。

    2021-12-07
    1
  • 🐮
    老师,你好,请教个问题,之前函数重载这块使用比较多的是通过编译器wrap方式重载,但这块只能对可执行文件进行重载,不能实现glibc重载,这里所说的LD_PRELOAD是不是也只是针对可执行文件进行重载不能实现像动态库重载,如果是某个glibc中的函数使用malloc,这种方案是不是不能进行重载的;

    作者回复: 文中的这种做法就是重载了glibc中的free函数。你可以自己动手试一下哦。

    2021-11-18
    2
    1
  • 流浪地球
    请问老师,tcmalloc的线程本地缓存会不会导致相同的一份代码产生的进程,分配的虚拟内存空间会大一些呢?看描述像是增加预分配了一些空间

    作者回复: 首先回答你的问题是,不会的。但我不理解你为什么会有这种误解,我怀疑你是哪个概念理解错了才会这样提问的。建议加微信群详细交流。

    2021-11-17
    4
  • 骨汤鸡蛋面
    void* p1 = malloc(16); free(p1) free的时候,根据参数知道从地址p1开始free,那如何知道free 16个字节呢?
    2022-07-11
    1
    1
  • 会爆炸的小米Note
    老师好!mmap是相当于只修改了页表项,把相应页面的页表项修改成已分配未缓存的状态,然后在访问的时候通过缺页中断加载到物理内存中吗? 第一节课中吊打面试官的部分中说通过mmap构建映射后通过遍历访问才能让内存commit 请教下老师有什么高效的方法来进行访问吗
    2022-03-06
    1
  • 佳伦
    malloc通过mmap从操作系统申请一块比较大的内存再精细化管理,但是mmap申请到的内存对于操作系统来说,也需要伙伴算法来管理。所以,内存管理经历了好几个层面,操作系统层面、用户层面,在硬件层可能还有类似的管理方法。所以套了这么多层效率肯定会受影响
    2022-03-05
    1
收起评论
显示
设置
留言
13
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部