栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

高级语言垃圾回收思路和如何减少性能影响原理分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

高级语言垃圾回收思路和如何减少性能影响原理分析

文章目录
    • 5. 高级语言垃圾回收的思路
      • 5.1 标记-清除
      • 5.2 标记-整理
      • 5.3 标记-复制
      • 5.4 在go 中使用的方式及原因
      • 5.5 怎么标记(从哪里开始找需要回收的对象)
        • 5.5.1 Root 节点进行广度优先搜索BFS
      • 5.6 串行GC步骤
      • 5.6 串行GC步骤引发的问题
      • 5.7 小结
    • 6. go如何减少gc对性能的影响
      • 6.1 三色标记法
      • 6.2 常规步骤
      • 6.3 并发标记出现的问题(删除)(其他协程是没有停的)
      • 6.4 Yuasa 删除屏障
      • 6.5 并发标记问题(插入)
      • 6.6 Dijkstra 插入屏障
      • 6.7 混合屏障
      • 6.8 小结
    • 7. 在实战中提高gc效率
      • 7.1 系统定时触发
      • 7.2 用户显式触发
      • 7.3 申请内存时触发
      • 7.4 GC 优化原则
      • 7.5内存池化
      • 7.6 减少逃逸
      • 7.7 使用空结构体
      • 7.8 GC 分析工具
      • 7.9 工具演示
      • 7.10 小结
    • 8. 总结
      • 8.1 协程栈
      • 8.2 堆内存结构
      • 8.3 堆内存分配
      • 8.4 堆内存回收(GC)
        • 8.4.1 标记-清除 法
        • 8.4.2 可达性分析 标记法
      • 8.5 并发GC
      • 8.6 GC 优化

5. 高级语言垃圾回收的思路

现在我们遇到了下面的情况,当几个对象需要回收时,编译器内部时怎么做的,虽然不用程序员去直接操作它,但是知道一下底层原理对后续帮助也是不小的


这里垃圾回收的思路,有下面三种方式:

  1. 标记-清除
  2. 标记-整理
  3. 标记-复制
5.1 标记-清除

这种是最简单粗暴的方式

下面直接展示在go 中当里昂个对象需要回收时,用标记-清楚的方式时如何处理的

清楚结束的状态:

【注】:如果是别的语言可能有碎片的问题,但是Go 使用的span分级策略,没有了这种问题

5.2 标记-整理

同样当有两个对象需要进行垃圾回收时,使用标记-整理时的处理方式时怎么样的

初始状态:标记了需要删除的对象

标记-整理: 这里的当时是把后面的对象整理过来让整个堆内存的碎片化情况大大改善但是开销很大,会造成GC 卡顿(老版本的java用的这种,因为之前的数据量不大,影响相对小一点)

5.3 标记-复制

同上的场景:

初始状态:

标记-赋值: 这里的方式是把有用的复制到另一块达到了整理的效果,用时还非常短但是空间消耗大

5.4 在go 中使用的方式及原因
  1. Go因为堆内存结构的独特优势,选择最简单的标记-清除, 后面两种是java 在用
  2. 找到有引用的对象,剩下的就是没有引用(剩下的清楚就行了)
5.5 怎么标记(从哪里开始找需要回收的对象)

在程序中,有一些对象是不能被清除的,下面是具体例子

  1. 被栈上的指针引用
  2. 被全局变量指针引用
  3. 被寄存器中的指针引用
    上述变量被称为 Root Set (GC Root)
5.5.1 Root 节点进行广度优先搜索BFS

这种方法也叫可达性分析标记法

具体步骤用下面几幅图描述:

  1. 下面中格子是系统中一个一个栈空间
  2. 字母表示的不同的对象,字母之间边的情况是不同对象之间的关系
  3. 开始进行BFS进行搜索
  4. 下一步搜索,先标记,后清除(这里就是清除对象G和H)
5.6 串行GC步骤

上面描述的垃圾回收是串行的,怎么理解呢,就是在扫描整个进程中的对象时,需要暂停其他所有协程,为什么,因为其他协程在运行时会影响这个垃圾回收机制的判断。
具体步骤是下面几步:

  1. Stop The World, 暂停所有其他协程
  2. 通过可达性分析,找到无用的堆内存
  3. 释放对内存
  4. 恢复所有其他协程
5.6 串行GC步骤引发的问题

STW 对性能影响大(暂停了其他协程,影响不必过多解释)

5.7 小结
  1. 从GC Root 出发,寻找被引用对象
  2. 没有被引用的就是无用对象
  3. 串行GC需要STW, 对性能影响大
6. go如何减少gc对性能的影响

通过对上面的例子进行分析,如果使用STW方式,对性能肯定是有很大影响的,这时就需要有一个减少gc对性能的影响的方法了

于是并发垃圾回收的方式就有了,难点在哪里呢,其实并发垃圾回收过程中 ,可以不难分析出难点在于标记阶段

6.1 三色标记法
  1. 黑色: 有用,已经分析扫描
  2. 灰色: 有用,还未分析扫描
  3. 白色: 暂时无用
6.2 常规步骤
  1. 初始状态
  2. 将灰色放入广度优先中队列中,进行下一步分析
  3. BFS 搜索


4. 清除白色对象
5. 再次标记时,所有对象回复为白色

6.3 并发标记出现的问题(删除)(其他协程是没有停的)
  1. 在并发标记进行中

  2. 业务: B指向C的指针释放

  3. 业务:E的一个指针成员指向了C

  4. 继续扫描

  5. C被错误清除

6.4 Yuasa 删除屏障

原理:

  • 并发标记时
  • 对指针释放的白色对象置灰

操作步骤:

  1. 并发标记初始状态

  2. 分析BF 之前,业务: B指向C的指针释放

  3. 删除屏障: 会强制C被置灰(只要释放了指针,强制置灰)

  4. 业务: E的一个指针成员指向了C

  5. 继续扫描

  6. 继续扫描,这样C就不会被误删除

特点:只要有用的对象就不会被误清理

删除屏障可以杜绝在GC标记中被释放的指针,被清理

6.5 并发标记问题(插入)

是指GC过程中只有插入过程,没有指针释放的过程(也就没有置灰的过程)
这时删除屏障没有起作用

出现问题步骤如下:

  1. 并发标记进行中, 标记前C已经是无用对象了

  2. 业务:E的一个指针成员指向了C

  3. 继续扫描

  4. 继续扫描

  5. 对象 C 被错误清除

6.6 Dijkstra 插入屏障

为了解决上面的问题
Dijkstra 插入屏障原理: 在并发标记时,对指针新指向的白色对象置灰

操作步骤:

  1. 并发标记进行中, 标记前C已经是无用对象了

  2. 业务:E的一个指针成员指向了C(插入屏障起效)

  3. 继续扫描

  4. 扫描结局,会发现没有之前的问题

插入屏障可以杜绝在GC标记中被插入的指针,被清理(保证新增的依赖关系不会被清理)

6.7 混合屏障

在Go中是使用上述两种屏障,也就是说将上面两种方式混合起来使用

  1. 被删除的堆对象标记为灰色
  2. 被添加的堆对象标记为灰色
6.8 小结
  1. 并发垃圾回收的关键在于标记安全
  2. 混合屏障机制兼顾了安全与效率
7. 在实战中提高gc效率

在go中如何提高gc效率呢,可以分析下面集中方式

  1. 系统定时触发gc
  2. 用户显式触发gc
  3. 申请内存时触发gc
7.1 系统定时触发
  1. sysmon 定时检查
  2. 如果2分钟内没有过GC,会触发
  3. 谨慎调整(因为这里涉及更改系统设置)

    2分钟;在runtime/proc.go 文件中
7.2 用户显式触发
  1. 用户调用runtime.GC 方法
  2. 并不推荐调用
7.3 申请内存时触发
  1. 给对象申请堆空间时,可能导致GC
7.4 GC 优化原则

尽量少在堆上产生垃圾

  1. 内存池化
  2. 减少逃逸(逃逸后变量在堆上)
  3. 使用空结构体
7.5内存池化
  1. 缓存性质的对象
  2. 频繁创建和删除
  3. 使用内存池,不GC
7.6 减少逃逸
  1. 逃逸会使原本在栈上的对象进入堆中
  2. fmt 包(在项目中少用,可以用log包)
  3. 返回了指针而不是拷贝
7.7 使用空结构体
  1. 空结构体指向一个固定地址
  2. 不占用堆空间
  3. 比如channel 传递空结构体
7.8 GC 分析工具
  1. go tool pprof
  2. go tool trace
  3. go build -gcflags = " -m"
  4. GODEBUG = " gctrace = 1" (简单粗暴)
7.9 工具演示

步骤:
配置环境变量
运行代码

7.10 小结
  1. GC主要由系统定时触发或者申请内存触发
  2. GC优化原则是减少在堆上产生垃圾
  3. 使用GC分析工具可以帮助分析GC问题
8. 总结 8.1 协程栈
  1. 协程栈记录了协程的执行现场
  2. Go协程栈位于Go堆内存上
  3. Go 使用参数拷贝传递
  4. 3种特殊情况下,变量可能会逃逸到堆上
  5. 1.14 以后,Go 使用连续栈,伸缩时直接使用新栈
8.2 堆内存结构
  1. Go模仿TCmalloc(C++的内存的结构),建立了自己的堆内存架构
  2. 使用heapArena 向操作系统申请内存
  3. 使用heapArena时,以mspan 为单位,防止碎片化
  4. mcentral 是mspan 们的中心索引
  5. mcache 记录了分配给各个P 的本地mspan
8.3 堆内存分配
  1. Go将对象按照大小分为3种
  2. 微小对象使用mcache
  3. mcache中的mspan 填满后,与mcentral 交换新的
  4. mcentral 不足时,在heapArena 开辟新的mspan
  5. 大对象直接在heapArena 开辟新的mspan
8.4 堆内存回收(GC) 8.4.1 标记-清除 法

标记有用对象,清除无用对象

8.4.2 可达性分析 标记法

从GC Root 出发,寻找被引用对象

8.5 并发GC
  1. 并发垃圾回收的关键在于标记安全
  2. 混合屏障机制兼顾了安全与效率
8.6 GC 优化
  1. GC主要由系统定时触发或者申请内存触发
  2. GC优化原则是减少在堆上产生垃圾
  3. 使用GC分析工具可以帮助分析GC问题
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1031415.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号