编译原理之美
宫文学
北京原点代码 CEO
46197 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
编译原理 · 期中考试周 (1讲)
编译原理之美
15
15
1.0x
00:00/00:00
登录|注册

33 | 垃圾收集:能否不停下整个世界?

课程小结
LLVM对垃圾收集的支持
增量收集和并发收集
分代收集(Generational Collection)
引用计数(Reference Counting)
停止和拷贝(Stop and Copy)
标记和清除(Mark and Sweep)
内存垃圾
垃圾收集算法

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

对于内存的管理,我们已经了解了栈和栈桢,在编译器和操作系统的配合下,栈里的内存可以实现自动管理。
不过,如果你熟悉 C 和 C++,那么肯定熟悉在堆中申请内存,也知道要小心维护所申请的内存,否则很容易引起内存泄漏或奇怪的 Bug。
其实,现代计算机语言大多数都带有自动内存管理功能,也就是垃圾收集(GC)。程序可以使用堆中的内存,但我们没必要手工去释放。垃圾收集器可以知道哪些内存是垃圾,然后归还给操作系统。
那么这里会有几个问题,也是本节课关注的重点:
自动内存管理有哪些不同的策略?这些策略各自有什么优缺点?
为什么垃圾收集会造成系统停顿?工程师们又为什么特别在意这一点?
相信学完这节课之后,你对垃圾收集的机制理解得会更加深刻,从而在使用 Java、Go 等带有垃圾收集功能的语言时,可以更好地提升回收效率,减少停顿,提高程序的运行效率。
当然,想要达到这个目的,你首先需要了解什么是内存垃圾,如何发现哪些内存是没用的?

什么是内存垃圾

内存垃圾是一些保存在堆里的对象,但从程序里已经无法访问。
在堆中申请一块内存时(比如 Java 中的对象实例),我们会用一个变量指向这块内存。这个变量可能是:全局变量、常量、栈里的变量、寄存器里的变量。我们把这些变量叫做 GC 根节点。它指向的对象中,可能还包含指向其他对象的指针。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

垃圾收集是现代计算机语言中的重要功能,通过自动管理堆内存,减少了程序员手动释放内存的工作。本文介绍了内存垃圾的特点和垃圾收集的两种经典算法:标记和清除以及停止和拷贝。标记和清除算法通过标记可达对象并清除不可达对象来进行垃圾收集,但可能存在内存碎片化问题。而停止和拷贝算法则将内存分为旧空间和新空间,通过拷贝可达对象来进行垃圾收集,但可能存在内存利用率不高和系统停顿时间长的问题。分代收集算法将注意力集中在比较“年轻”的数据上,以提高垃圾收集效率。增量收集和并发收集算法则致力于缩短垃圾收集的停顿时间,通过增量式地运行和并发执行垃圾收集工作来提高系统的实时性。这些算法的实现需要编译器的帮助,通过读屏障、写屏障和安全点机制来保证垃圾收集的正确性和不影响程序执行。通过本文的学习,读者可以更深入地了解垃圾收集的机制,从而在使用带有垃圾收集功能的语言时,提升回收效率,减少停顿,提高程序的运行效率。文章还介绍了LLVM对垃圾收集的支持,包括创建安全点、计算栈图、提供写屏障和读屏障的支持,以及与编译器的配合。总之,本文深入探讨了垃圾收集的原理、常见算法和LLVM的支持,为读者提供了全面的了解和实践指导。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编译原理之美》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • 沉淀的梦想
    看了很多关于并发收集的文章,但是还是云里雾里的,感觉得对照着GC的实现源码学习才能明白

    作者回复: 推荐你看两个GC的源代码:一个是Lua的,一个是Juila的。我最近时间不充裕,不够时间自己写一个GC。但我后面会补上。

    2019-11-08
    12
  • Huayra
    Go的GC不是分代的,Java的是;使用的是并发三色标记算法。 GC可以看看《垃圾回收的算法与实现》这本书。 Go的STW 在1.8版本已经优化到毫秒以下了。

    作者回复: 感谢分享。 GC是实现一门现代语言最大的挑战之一。毫秒级以下的停顿确实令人赞叹。

    2020-02-06
    8
  • LEGEND OF HEART
    曾经在生产环境遇到过GC的问题,我们做的是一个数据监控系统。当时的现象是,程序偶现会在某些时间点丢数据,然后程序的粒度是秒级别的,所以丢数据用户感知会很强烈。一开始是怀疑程序bug导致数据采集错误,但是想了很久,查了日志,做了很多测试都没有复现。后来想到是不是GC影响的,用GC Viewer查看了GC日志,果然,发现了程序耗费了很多时间在full GC上,占了运行时间的42.8%。基本明白了是GC的问题之后,我就开始想,是不是程序分析的无用数据太多了?还是程序的运行内存开的太小了?还是说垃圾收集器选择的有问题?试了一下,清理了一些无用数据,并将程序内存调大,运行了一段时间,gc的次数和时间明显减少了。上面的案例,算是一次简单的GC问题定位吧,后续其实还是出现了几次GC问题,仿照上面的思路,看看GC日志,基本也可以把问题找出来。

    作者回复: 非常感谢你的经验分享!GC是调优的重要环节。

    2019-11-08
    7
  • Dylan
    "在拷贝内存之后,你需要修改所有指向这块内存的指针。像 C、C++ 这样的语言,因为内存地址是对编程者可见的,所以没法采用停止和拷贝算法。" 这里有点没太明白,既然内存地址对开发人员可见,那应该是修改指针地址啊

    作者回复: 不行呀,这可能造成程序的错误。 在Java、Go等语言里,在运行时有办法知道哪个数值其实是引用(也就是指针)。 在C语言里,一个指针和一个整数编译完毕以后没啥区别。编译器无法判断哪个是指针,哪个其实只是个普通数值。

    2020-03-28
    3
  • 余晓飞
    这时,新空间里所有对象整齐排列,没有内存碎片。 这里的“新空间”应该改为“旧空间”。

    作者回复: 拷贝完毕以后,新旧空间会互换。这里说的“旧空间”,指的是互换完毕之后的。

    2020-01-16
    1
  • 余晓飞
    整个标记过程的直观示意图如下 这句话下面的图中似乎缺少了一步: 标记D,逆转A->D的指针

    作者回复: 你看得很仔细,少上传了一张图。感谢帮忙校正!

    2020-01-15
  • sugar
    补充分享:JavaScript的v8虚拟机也实现了本文中宫老师所提到的诸多gc算法,旨在优化js的gc问题。v8是分代收集,老生代对象有使用标记清除和标记整理,并且也有应用本文后半段所提到的三色算法来实现并发和增量gc。另外,v8背后的作者lars bak其实就是早年开发某一款java虚拟机的,在采访中他也提到了gc是他认为非常“好玩”的一个领域,因为你永远可以找到更合适的回收时间点。供各位参考~
    2020-05-06
    2
    4
  • Samaritan.
    老师,请问下,给一个变量赋予新的地址是怎么实现的呢? 比如我定义一个变量:int var; 感觉var这个变量对应的虚拟地址,在编译完成后应该就定下来了吧?
    2023-01-23归属地:福建
    2
  • wahaha
    商业版Java实现Azul Platform Prime号称无停顿GC: https://www.azul.com/products/pricing/ C4 pauseless garbage collector for nearly any size heap (1GB to 8TB) Hyper-optimized Falcon JIT compiler (LLVM) ReadyNow! warmup accelerator GraalVM发布一段时间后,把Java编译为本地代码的工具Excelsior Jet的网站就下线了,但Azul还在,说明Azul的实力不小。
    2021-10-16
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部