用户程序通过内存分配器(Allocator)在堆上申请内存,而垃圾收集器(Collector)负责回收堆上的内存空间,内存分配器和垃圾收集器共同管理程序中的堆内存空间。

基本概念

垃圾分类

  • 语义垃圾:也就是内存泄漏,指的是从语法上可达的对象,也就是被其他对象引用的,但是从语义上来讲是垃圾。这类垃圾,垃圾回收是不管的
  • 语法垃圾:从语法上是不可达的对象,也就是没有对象引用了,这些垃圾是垃圾回收重点照顾的对象

垃圾回收算法

常见的垃圾回收算法:

  • 引用计数:

    某个对象的根引用计数变为0时,其所有节点均需被回收

  • 标记压缩:

    存活对象移动到一起,有效解决内存碎片问题

  • 复制算法:

    将所有正在使用的对象从From空间复制到To空间,堆利用率只有一半,也能解决内存碎片问题

  • 标记清除:

    标记垃圾对象,然后再清除。解决不了内存碎片问题,需要与能尽量避免内存碎片的内存分配器使用,如tcmalloc

标记清除

标记清除算法是最常见的垃圾收集算法,标记清除收集器是跟踪式垃圾收集器,其执行过程可以分为标记、清除两个阶段:

  1. 标记阶段:从根对象出发查找并标记堆中所有存活的对象
  2. 清除阶段:遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表

传统的标记清除算法,垃圾收集器从垃圾收集的根对象出发,递归遍历这些对象指向的子对象并将所有可达的对象标记成存活。标记结束后,垃圾收集器会依次遍历堆中的对象并清除其中的垃圾,整个过程需要标记对象的存活状态,所以用户程序在垃圾收集的过程中不能执行,也就是常说的STW(Stop The World)

三色抽象

为了解决原始标记清除算法带来的长时间STW,大部分追踪式垃圾收集器都会实现三色标记算法的变种以缩短STW的时间

三色标记算法将程序中的对象分成白色、黑色、灰色:

  • 白色对象:潜在的垃圾,也就是没有被扫到的对象,其内存可能会被垃圾收集器回收
  • 黑色对象:活跃的对象,包括不存在任何引用外部指针的对象以及从根对象可达的对象
  • 灰色对象:活跃的对象,因为存在指向白色对象的外部指针,垃圾收集器会扫描这些对象的子对象

标记过程:

垃圾收集器开始工作的时候,不存在任何黑色对象,根对象会被标记成灰色,垃圾收集器只会从灰色对象集合中取出对象开始扫描,当灰色集合中不存在任何对象时,标记就会结束。

大致工作原理:

  1. 从灰色对象的集合中选择一个灰色对象并将其标记成黑色
  2. 将黑色对象指向的所有对象都标记成灰色,保证该对象和被该对象引用的对象都不会被回收
  3. 重复上述两个步骤直到不存在灰色对象

当三色标记结束后,应用程序的堆中就不存在任何灰色对象,我们只能看到黑色的存活对象以及白色的垃圾对象,垃圾收集器可以回收这些白色的垃圾。

缺陷:

由于用户程序可能在标记执行的过程中修改对象的指针,所以三色标记清除算法本身是不可以并发或者增量执行的,仍需要STW。如果本来不应该被回收的对象被回收了,这在内存管理中是非常严重的错误,这种错误称为悬挂指针,也就是指针没有指向特定类型的合法对象,影响了内存的安全性,想要并发或者增量标记对象就需要使用屏障技术。

屏障技术

内存屏障技术是一种屏障指令,可以让CPU或编译器在执行内存相关的操作时遵循特定的约束,目前多数的现代处理器都会乱序执行指令以最大化性能,但是该技术能够保证内存操作的顺序性,在内存屏障前执行的操作一定会先于内存屏障后执行的操作。想在并发或增量的标记算法中保证正确性,需要达到两种三色不变性的其中一种。

三色不变性:

  • 强三色不变性:黑色对象不会指向白色对象,只会指向灰色对象或者黑色对象
  • 弱三色不变性:黑色对象指向的白色对象必须包含一条从灰色对象经由多个白色对象的可达路径

屏障技术分为读屏障和写屏障,但是由于读屏障需要在读操作中加入代码片段,所以对用户程序的性能影响较大。解析一下go语言中使用的两种写屏障技术,插入写屏障和删除写屏障。

插入写屏障(DIJKSTRA)

writePointer(slot, ptr):
shade(ptr)
*slot = ptr

每当要执行*slot = ptr时,会先执行写屏障通过shade函数尝试改变指针颜色。如果ptr指针是白色,那么会将该对象设置成灰色。

这张图中可以看到,在一次正常的标记过程中,发生了用户程序修改了指针引用(或者新插入了一个引用关系)的情况,如果我们采用 插入写屏障 我们就需要将新指向的对象标为灰色,以此保证强三色不变性。(对于新指向的对象来说,属于被插入一个引用,所以叫插入写屏障)

在插入写屏障中,我们的标记过程变成(关键第2步):

  1. 基本标记流程不变,我们快进到垃圾收集器将根对象指向A对象标记成黑色,并将A对象指向的对象B标记成灰色。
  2. 这时用户程序修改A对象的指针,将原本A对象指向B对象的指针指向C对象,这个时候就会触发写屏障将C对象标记为灰色,避免被错误回收。
  3. 垃圾收集器依次遍历程序中的其他灰色对象,将他们分别标记成黑色

删除写屏障(YUASA)

writePointer(slot, ptr)
shade(*slot)
*slot = ptr

删除写屏障会在老对象的引用被删除的时候,将白色的老对象涂成灰色,这样就可以保证弱三色不变性,老对象引用的下游对象一定可以被灰色对象引用。

使用删除写屏障技术的垃圾收集器和用户程序交替运行的场景中的标记过程:

  1. 垃圾收集器将根对象指向A对象标记成黑色并将A对象指向的对象B标记成灰色
  2. 如果这时用户程序将B指向C的指针删除,那么C触发删除写屏障,由于C是白色,所以被涂成灰色
  3. 垃圾收集器依次遍历程序中的其他灰色对象,将他们分别标记成黑色

第二步触发删除写屏障的着色,因为删除了B指向C的指针,所以C和D分别违反强三色不变性和弱三色不变性,着色后保证了三色不变性,避免悬挂指针。

简单来说就是,在改变指针指向的时候,原来指向的那个对象是白色的话就要变成灰色,以此保证弱三色不变性。(对于老对象来说,引用关系被解除了,所以叫删除写屏障)

增量和并发

两种策略优化垃圾收集器不会以为回收垃圾导致长时间STW:

  • 增量垃圾收集:增量得标记和清除垃圾,降低应用程序暂停的最长时间
  • 并发垃圾手机:利用多核的计算资源,在用户程序执行时并发标记和清除垃圾

由于两种方式都需要垃圾收集器与用户程序交替执行,所以需要配合屏障技术。

增量收集器

增量收集器将原本时间较长的暂停时间切分成多个更小的GC时间片。增量收集器需要配合三色标记法和屏障技术一起使用。将GC过程分段执行,虽然拉长了总的垃圾回收时间,但是减少了程序STW的时间。不过写屏障还是有些开销的。

并发收集器

利用多核优势,将GC过程与用户程序并行执行(大部分情况下),也是要配合屏障技术。

References

  1. https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/
  2. https://spin.atomicobject.com/2014/09/03/visualizing-garbage-collection-algorithms/

GO的GC辣鸡回收(一)的更多相关文章

  1. JVM 辣鸡回收

    垃圾回收算法 标记清除法 先标记出需要回收的对象,然后一次性回收.缺点:会产生内存碎片,并且效率也不高. 标记压缩法 先标记出需要回收的对象,然后让存活对象向一端移动,移动的过程中进行回收辣鸡.避免了 ...

  2. JVM学习(4)——全面总结Java的GC算法和回收机制

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...

  3. java: system.gc()和垃圾回收机制finalize

    System.gc()和垃圾回收机制前的收尾方法:finalize(收尾机制) 程序退出时,为每个对象调用一次finalize方法,垃圾回收前的收尾方法 System.gc() 垃圾回收方法 clas ...

  4. 关于GC进行垃圾回收的时机

    前言 今天查看一个同事的代码,发现代码中多处地方使用了GC.Collect()方法,我问他为什么这么做,他说感觉程序中定义了好多变量,怕GC回收不及时,用GC.Collect()可以手动掌控GC进行垃 ...

  5. GC(垃圾回收)

    Java程序的内存分配和回收都是由JRE在后台自动进行的.JRE会负责回收那些不再使用的内存,这种机制被称为垃圾回收GC.通常JRE会提供一条超级线程来进行检测和控制,一般都是在CPU空闲或内存不足时 ...

  6. 【转载】Java性能优化之JVM GC(垃圾回收机制)

    文章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我 ...

  7. JVM总括二-垃圾回收:GC Roots、回收算法、回收器

    JVM总括二-垃圾回收:GC Roots.回收算法.回收器 目录:JVM总括:目录 一.判断对象是否存活 为了判断对象是否存活引入GC Roots,如果一个对象与GC Roots没有直接或间接的引用关 ...

  8. JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...

  9. Java性能优化之JVM GC(垃圾回收机制)

    Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...

随机推荐

  1. informix数据库分页

    需求描述 当查询结果返回大量数据情况下,比如报表查询.需要按一定条件排序提供分页呈现数据. INFORMIX实现方案:Informix 数据库提供了非常便捷.高效的SQL. SELECT SKIP M ...

  2. vscode配置java+gradle开发环境

    1.安装扩展包Java Extension Pack,里面包含java开发所必须的扩展 2.安装java jdk,8版本就是1.8版本,根据需要安装不同的版本 3.下载gradle,将bin文件夹添加 ...

  3. 【动画消消乐 】仿ios、android中常见的一个loading动画 074

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  4. 学习笔记-CCS-MSP430F5529[快速入门篇二]

    由于2021的全国电赛延期了,从今天开始打算好好整理一下使用CCS编程的经验,本篇笔记会好好整理一下我备赛期间用CCS写的程序,包括外部中断,定时器部分的定时中断,定时器输入捕获,PWM波输出,UAR ...

  5. 记一次 GitLab 的迁移过程

    目录 1. 迁移背景 2. GitLab 整体架构介绍 3. GitLab 安装 配置选择 安装方式选择 安装的网络区域 安装 GitLab GitLab 常用命令 配置管理员账号密码 4. 配置 G ...

  6. 安卓安装https证书

    前置条件 1 手机要设置密码 然后安装charles 证书 2 赋予 adb shell root权限(安装magisk就行) adb shell # 连接手机进入shell模式 su root # ...

  7. Java互联网架构师系统进阶课程学习 (3)【享学】

    3.原子操作CAS Atom(不可分割) 什么是原子操作?如何实现原子操作? syn基于阻塞的锁的机制,1.被阻塞的线程优先级很高,2.拿到锁的线程一直不释放锁怎么办?3.大量的竞争,消耗cpu,同时 ...

  8. Docker入门第三章

    配置阿里云镜像加速器 1.首先打开阿里云,搜索容器镜像服务,打开如下 2.配置镜像加速器 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.j ...

  9. 通过Appium日志,分析其运行原理

    1.启动appium: appium的rest http 接口开始监听 4723 端口: 2.运行 python 脚本: appium接收到一个post请求 /wd/hub/session,并携带了 ...

  10. Redis缓存哪些事儿

    一提到Redis缓存,我们不得不了解的三个问题就是:缓存雪崩.缓存击穿和缓存穿透.这三个问题一旦发生,会导致大量的请求直接请求到数据库层.如果并发压力大,就会导致数据库崩溃.那p0级的故障是没跑了. ...