当前位置: 首页 > news >正文

GC参考手册 —— GC 算法(基础篇)

本章简要介绍GC的基本原理和相关技术, 下一章节再详细讲解GC算法的具体实现。各种垃圾收集器的实现细节虽然并不相同,但总体而言,垃圾收集器都专注于两件事情:

  • 查找所有存活对象
  • 抛弃其他的部分,即死对象,不再使用的对象。

第一步, 记录(census)所有的存活对象, 在垃圾收集中有一个叫做 标记(Marking) 的过程专门干这件事。

标记可达对象(Marking Reachable Objects)

现代JVM中所有的GC算法,第一步都是找出所有存活的对象。下面的示意图对此做了最好的诠释:

首先,有一些特定的对象被指定为 Garbage Collection Roots(GC根元素)。包括:

  • 当前正在执行的方法里的局部变量和输入参数
  • 活动线程(Active threads)
  • 内存中所有类的静态字段(static field)
  • JNI引用

其次, GC遍历(traverses)内存中整体的对象关系图(object graph),从GC根元素开始扫描, 到直接引用,以及其他对象(通过对象的属性域)。所有GC访问到的对象都被 标记(marked) 为存活对象。

存活对象在上图中用蓝色表示。标记阶段完成后, 所有存活对象都被标记了。而其他对象(上图中灰色的数据结构)就是从GC根元素不可达的, 也就是说程序不能再使用这些不可达的对象(unreachable object)。这样的对象被认为是垃圾, GC会在接下来的阶段中清除他们。

在标记阶段有几个需要注意的点:

在标记阶段,需要暂停所有应用线程, 以遍历所有对象的引用关系。因为不暂停就没法跟踪一直在变化的引用关系图。这种情景叫做 Stop The World pause (全线停顿),而可以安全地暂停线程的点叫做安全点(safe point), 然后, JVM就可以专心执行清理工作。安全点可能有多种因素触发, 当前, GC是触发安全点最常见的原因。

此阶段暂停的时间, 与堆内存大小,对象的总数没有直接关系, 而是由存活对象(alive objects)的数量来决定。所以增加堆内存的大小并不会直接影响标记阶段占用的时间。

标记 阶段完成后, GC进行下一步操作, 删除不可达对象。

删除不可达对象(Removing Unused Objects)

各种GC算法在删除不可达对象时略有不同, 但总体可分为三类: 清除(sweeping)、整理(compacting)和复制(copying)。下一章节将详细讲解这些算法。

Sweep(清除)

Mark and Sweep(标记-清除) 算法的概念非常简单: 直接忽略所有的垃圾。也就是说在标记阶段完成后, 所有不可达对象占用的内存空间, 都被认为是空闲的, 因此可以用来分配新对象。

这种算法需要使用 空闲表(free-list),来记录所有的空闲区域, 以及每个区域的大小。维护空闲表增加了对象分配时的开销。此外还存在另一个弱点 —— 明明还有很多空闲内存, 却可能没有一个区域的大小能够存放需要分配的对象, 从而导致分配失败(在Java 中就是 OutOfMemoryError)。

Compact(整理)

标记-清除-整理算法(Mark-Sweep-Compact), 将所有被标记的对象(存活对象), 迁移到内存空间的起始处, 消除了标记-清除算法的缺点。 相应的缺点就是GC暂停时间会增加, 因为需要将所有对象复制到另一个地方, 然后修改指向这些对象的引用。此算法的优势也很明显, 碎片整理之后, 分配新对象就很简单, 只需要通过指针碰撞(pointer bumping)即可。使用这种算法, 内存空间剩余的容量一直是清楚的, 不会再导致内存碎片问题。

Copy(复制)

标记-复制算法(Mark and Copy) 和 标记-整理算法(Mark and Compact) 十分相似: 两者都会移动所有存活的对象。区别在于, 标记-复制算法是将内存移动到另外一个空间: 存活区。标记-复制方法的优点在于: 标记和复制可以同时进行。缺点则是需要一个额外的内存区间, 来存放所有的存活对象。

转载于:https://www.cnblogs.com/java-chen-hao/p/10648780.html

相关文章:

  • java B2B2C Springboot电子商城系统-路由网关(zuul)
  • 我们用5分钟写了一个跨多端项目
  • Ubuntu MATE 推出树莓派版本
  • 【本人秃顶程序员】SpringBoot基础之banner玩法解析
  • 红米6.0系统设备最完美激活Xposed框架的流程
  • 微软宣布Azure Functions正式支持Java
  • 常用网络设备
  • Mysql5.7 - 一键安装脚本
  • 一、python小功能记录——监听键盘事件
  • note_4.10
  • jstl使用中的错误----基于idea
  • python 计算机基础
  • 数据流中的中位数(未)
  • jeecg入门操作—菜单管理
  • 解决AutoComplete数据过多时的卡顿问题
  • 《深入 React 技术栈》
  • C++11: atomic 头文件
  • Druid 在有赞的实践
  • Iterator 和 for...of 循环
  • javascript数组去重/查找/插入/删除
  • leetcode388. Longest Absolute File Path
  • nginx 配置多 域名 + 多 https
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • ubuntu 下nginx安装 并支持https协议
  • Vue.js-Day01
  • 码农张的Bug人生 - 见面之礼
  • 批量截取pdf文件
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 通过git安装npm私有模块
  • 用简单代码看卷积组块发展
  • 在weex里面使用chart图表
  • 正则表达式
  • Linux权限管理(week1_day5)--技术流ken
  • RDS-Mysql 物理备份恢复到本地数据库上
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • (13)Hive调优——动态分区导致的小文件问题
  • (6)STL算法之转换
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (ros//EnvironmentVariables)ros环境变量
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (利用IDEA+Maven)定制属于自己的jar包
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET Framework与.NET Framework SDK有什么不同?
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .NET企业级应用架构设计系列之应用服务器
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • @Bean有哪些属性
  • [20180129]bash显示path环境变量.txt
  • [20190113]四校联考
  • [AIGC] Redis基础命令集详细介绍
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • [Android Studio] 开发Java 程序