JVM 垃圾收集算法 标记-清楚、标记-复制、标记-整理
摘要
Java程序在运行过程中会产生大量的对象,但是内存大小是有限的,如果光用而不释放,那内存迟早被耗尽。如C、C++程序,需要程序员手动释放内存,Java则不需要,是由垃圾回收器去自动回收。
垃圾回收器回收内存至少需要做两件事情:标记垃圾、回收垃圾。于是诞生了很多算法及垃圾回收器。
垃圾判断算法
即判断JVM中的所有对象,哪些对象是存活的,哪些对象可回收的算法。
引用计数算法
在对象中添加一个属性用于标记对象被引用的次数,每多一个其他对象引用,计数+1,当引用失效时,计数-1,如果计数=0,表示没有其他对象引用,就可以被回收。
这个算法无法解决循环依赖的问题。

可达性分析算法
通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系链向下搜索,如果某个对象无法被搜索到,则说明该对象无引用执行,可回收。相反,则对象处于存活状态,不可回收。
JVM中的实现是找到存活对象,未打标记的就是无用对象,GC时会回收。

哪些对象可以作为GC Root呢:
- 所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
- VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
- JNI handles,包括global handles和local handles
- (看情况)所有当前被加载的Java类
- (看情况)Java类的引用类型静态变量
- (看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)
- (看情况)String常量池(StringTable)里的引用
垃圾回收算法
1、标记-清除算法
概念:
顾名思义,标记-清除算法分为两个阶段,标记(mark)和清除(sweep)。
标记:遍历所有的GC Roots,然后将所有的GC Roots可达的对象标记为存活的对象。
清除:清除的过程将遍历所有堆中的对象,将没有标记的对象全部清除。
图解:

对上图中的黄色部分进行垃圾回收,回收后的截图如下所示:

从图中可知,进行标记清理后,可用内存增加,但是清除垃圾后的内存地址不连接,出现垃圾碎片。
缺点:
1、执行效率不稳定,如果Java堆中包含大量对象,而且大部分是需要被回收的,这时必须记性大量标记及清除动作,导致标记和清除两个过程执行效率都随对象数量增长而降低。
2、内存空间碎片化的问题,标记、清除后会产生大量的不连续内存碎片,空间碎片太可能会导致当以后需要分配大对象时无法找到足够的连续内存二不得不提前触发另一次垃圾收集动作。
2、标记-复制算法
概念:
复制算法将内存分为两个区间,这两个区间是动态的,在任意一个时间点,所有分配的对象内存只能在其中一个区间(活动区间),另外一个区间就是空闲区间。
当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址一次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。这个时候空闲内存已经变成了活动区间,垃圾对象全部在原来的活动区间,清理掉垃圾对象,原活动区间就变成了空闲区间。
这种方式内存的代价太高,每次基本上都要浪费一半的内存。于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存是Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代)。
图解:


优点:
1、很好地解决了“标记-清除”算法,内存布局混乱的缺点。
缺点:
1、浪费一半的内存。
2、假设对象存活率为100%,那么“标记-复制”算法的GC过程就是重复的把对象复制一遍,而且将所有的引用地址重置一遍。可以预见的复制所消耗的时间随着对象存活率达到一定程度将会变成灾难。所以“标记-复制”算法使用的场景是可以忍受只是用50%内存,对象存活率非常低
3、标记-整理算法
概念:
标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
图解:


优点:
1、弥补了“标记-清除”算法,内存区域分散的缺点
2、弥补了“标记-复制”算法内存减半的代价
缺点:
1、效率不高,对于“标记-清除”而言多了整理工作。
4、分代收集算法
当前商业虚拟机的垃圾收集都采用分代收集。此算法没啥新鲜的,就是将上述三种算法整合了一下。具体如下:
根据各个年代的特点采取最适当的收集算法
1、在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。只需要付出少量存活对象的复制成本就可以完成收集。
2、老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须用标记-清除或者标记-整理。
测试案例
以下测试采用的是Serial加Serial Old收集器组合。
查看当前jdk默认额收集器使用以下语句。
java -XX:+PrintCommandLineFlags -version
1、对象优先在Eden分配
测试代码
2、大对象直接进入老年代
3、长期存活的对象将进入老年代
4、动态年龄判断
5、空间分配担保
JVM 垃圾收集算法 标记-清楚、标记-复制、标记-整理的更多相关文章
- JVM垃圾收集算法(标记-清除、复制、标记-整理)
[JVM垃圾收集算法] 1)标记-清除算法: 标记阶段:先通过根节点,标记所有从根节点开始的对象,未被标记的为垃圾对象(错了吧?) 清除阶段:清除所有未被标记的对象 2)复制算法: 将原有的内存空间 ...
- JVM垃圾收集算法之标记算法
前言 总所周知,jvm的垃圾收集算法一般包括标记.清除.整理三个阶段,最近在看了有关于垃圾收集的标记算法,记录一下自己的理解. 垃圾收集中标记算法有两种:一种是引用计数法,一种是根搜索算法. 引用记数 ...
- JVM垃圾收集算法
JVM垃圾收集 1. 判断对象是否存活 引用计数算法 对象添加一个引用计数器,每个地方引用它,计数器值加+1:当引用失效,计算器值减1:任何时刻计数器为0的对象不可能被使用.引用计数算法实现简单,高效 ...
- JVM垃圾收集算法之清除算法
最近看了一些大佬的博文,文中提到说:学习知识不能一味的死学滥学,在学之前要明白为什么要学这个知识,在实际的应用中怎么运用这个知识.我觉得说的很对,很多时候我学习确实是了解了这是什么原理,但是要说到实际 ...
- JVM——垃圾收集算法及垃圾回收器
一.垃圾回收算法 1.标记-清除算法 1)工作流程 算法分为"标记"和"清除"阶段:首先标记出所有需要回收的对象(标记阶段),在标记完成后统一回收所有被标记的对 ...
- JVM——垃圾收集算法
1.标记-清除算法 最基础的收集算法,如其名,算法为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 两个不足: 1)效率问题,标记和清除两个过程的效率 ...
- 【004】【JVM——垃圾收集算法】
Java虚拟机学习总结文件夹 垃圾收集算法 垃圾收集算法的实现涉及大量的程序细节,并且各个平台的虚拟机操作内存的方法又各不同样,介绍几种垃圾收集算法的思想及其发展过程. 标记-清除算法 垃圾收集 ...
- [jvm] -- 垃圾收集算法篇
垃圾收集算法 标记-清除算法 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 缺点: 效率问题: 空间问题(标记清除后会产生大量不连续的碎片) 复制算法 它可以将内存分为大小相同 ...
- JVM垃圾收集算法——分代收集算法
分代收集算法(Generational Collection): 当前商业虚拟机的垃圾收集都采用"分代收集算法". 这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分 ...
随机推荐
- Linux常用命令之ls、cd、pwd、mkdir命令讲解
ls命令令是Linux最常用的命令之一,也是一条非常古老的命令.在开始学习Linux命令之前,还是想给大家一条建议: 很多同学可能刚开始学习Linux,然后就去买一些教材去学习,教材上面有可能收集了L ...
- Spring Cloud及微服务简介
最近在看微服务编排的东西,看到一篇入门博客,私以为不错,再次分享下:https://blog.csdn.net/w05980598/article/details/79007194 什么是微服务 微服 ...
- Linux下利用docker搭建elasticsearch(单节点)
1. 拉取镜像 #elasticsearch 6.x和7.x版本有很多不一样需要确认 docker pull docker.elastic.co/elasticsearch/elasticsearch ...
- 【Java并发知识点汇总】
1,ArrayList 为什么线程不安全(https://www.jianshu.com/p/41be1efe5d65)
- 趣讲 PowerJob 超强大的调度层,开始表演真正的技术了
本文适合有 Java 基础知识的人群 作者:HelloGitHub-Salieri HelloGitHub 推出的<讲解开源项目>系列. 写在前面的碎碎念:终于到了万众期待的调度层原理了. ...
- Django REST framework 单元测试
Django REST framework 单元测试 只是简单记录一下测试代码怎么写 环境 Win10 Python3.7 Django2.2 项目 参照官网 快速开始 写了一个 demo 测试 参照 ...
- 商业分析-04行为&业务相关数据指标
[访问深度]用户对产品的了解程度 [弹出率] 弹出率是基于访问回话的 而不是基于页面的,上图中1 4 6 是属于弹出
- C#设计模式之18-备忘录模式
备忘录模式(Memento Pattern) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/421 访问. 备忘录模式属 ...
- Flutter 容器Container类和布局Layout类
1.布局和容器 [布局]是把[容器]按照不同的方式排列起来. Scaffold包含的主要部门:appBar,body,bottomNavigator 其中body可以是一个布局组件,也可以是一个容器组 ...
- 用它5分钟以后,我放弃用了四年的 Flask
有一个非常简单的需求:编写一个 HTTP接口,使用 POST 方式发送一个 JSON 字符串,接口里面读取发送上来的参数,对其中某个参数进行处理,并返回. 如果我们使用 Flask 来开发这个接口,那 ...