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

关于Java垃圾回收问题的总结

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

关于Java垃圾回收问题的总结

关于Java垃圾回收问题的总结

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录
  • 关于Java垃圾回收问题的总结
  • 前言
  • 一、垃圾
    • 1、什么是垃圾
    • 2、垃圾判断算法
        • 引用计数法
        • 可达性分析
    • 引用
    • 2、垃圾回收算法
    • 3、垃圾回收器


前言

Java语言的可移植性建立在JVM的基础上的,只要任何平台装有Java虚拟机,字节码文件,就可以在这个平台上运行,就这实现了“一次编译,多次运行”。作为Java语言最重要特性之一的自动垃圾回收机制,也是基于JVM实现的。那么他是怎么实现的呢?


一、垃圾 1、什么是垃圾

在Java中所有对象都是存放在堆中的,所以垃圾回收也是主要在堆中进行的。在JVM看来,垃圾是在堆中存在并且已经“死亡”的对象。那什么是“死亡”呢?我们可以简单地理解为不被任何途径使用的对象,那怎么样来判断对象的“死亡”呢?这就涉及到垃圾判别算法,主要有引用计数法和可达性分析。

2、垃圾判断算法 引用计数法

在这种算法中,给每个对象都设置一个引用计数器,当一个地方引用该对象时,引用计数器就+1;当引用失效时,引用计数器就-1;当引用计数器为0时,说明这个对象没有被任何对象引用,也就是垃圾,就可以被收集了。

· 优点:实现起来较为简单,对程序不被长时间打断有利。
· 缺点:需要额外的空间来储存计数器;无法解决循环引用问题,当A引用B,B也引用A的时候,此时A和B对象的引用都不为0,此时就无法进行垃圾回收。所以主流虚拟机都不采用此方法。

可达性分析

可达性分析也叫根搜索法,一个对象只有满足一下两个对象之一才会被成为可达的:
· 对象被一个可达对象引用
· 对象属于根集中的对象

在Java中,会将以下对象标记为根集对象:
· 虚拟机栈中引用的对象
· 方法区静态属性引用的对象
· 方法区常量池引用的对象
· 本地方法区JNI引用的对象
· 活跃的线程
根集的对象被称为GC Roots,从GC Roots开始向下搜索,搜索走过的路称为引用链,如果一个对象到GC Roots没有用任何引用链链接,那么这个对象就是不可达的。反之则为可达的。

· 优点:解决了循环引用问题,且不占用额外的空间
· 缺点:多线程场景下,其他线程可能会更新已经访问过的对象的引用


引用

在Java中有四种引用类型:
1、 强引用:比如 Object obj = new Object(),这类是Java中最常见的引用。只要引用还存在,就永远不会收集。
2、 软引用:它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。
3、 弱引用:它也是用来描述非必须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
4、 虚引用:也称为幻引用,最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。

2、垃圾回收算法

标记清除法:第一步:利用可达性遍历内存,把存活对象和垃圾对象进行标记。
第二步:在遍历一遍,把垃圾对象回收掉。
特点:效率不太好,标记和清除的效率都不太高,标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行需要分配较大的对象而找不到储存空间而在进行一次GC。

· 特点:效率不太好,标记和清除的效率都不太高,标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行需要分配较大的对象而找不到储存空间而在进行一次GC。

标记整理法:
第一步:利用可达性遍历内存,把存活对象和垃圾对象进行标记。
第二步:将所有存活对象向一端移动,把端边界以外的对象全部清除掉。

· 特点:适用于存货对象多,垃圾少的情况下,无空间碎片产生。

复制算法 :

将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就把还存活着的对象转移到另一块,再把使用的内存清除。

· 特点:标记阶段和复制阶段可以同时进行;每次只对一块内存进行回收,运行高效,不会产生空间碎片,但是内存使用效率极低

分代收集:
Java虚拟机一般将内存划分为新生代和老年代,在新生代中,有大量死去和少量存活对象,所以采用复制算法,因为只需要付出少量存活对象的复制成本就可以完成收集。老年代中存活对象占大多数,所以采用标记清除或整理法进行回收。

· 新生代:几乎所有新生成的对象都放在新生代中。新生代的内存按照8:1:1的比例分为Eden区和Survivor0区和Survivor1区;大部分对象放在Eden区但是当Eden区空间不足时,则会发起一次GC。回收时先将Eden区存活的对象放在Survivor0 区,然后清空Eden区,然后当Survivor0区也放满了,就把Eden区和Survivor0区存活的对象放在Survivor1区中,然后清空Eden区和Survivor0区,此时Survivor0区为空,交换Survivor0区和Survivor1区,保持Servivor1区为空。当Survivor1区不足以放下Eden区和Survivor0区存活的对象时,就会将存活的对象放进老年代里。当对象在Survivor躲过一次GC时,其年龄就会+1,默认情况下当年龄达到15岁时就会移动到老年代。如果老年代也存放满,就会触发一次Full GC,也就是新生代和老年代都会进行垃圾回收。
· 老年代:在新生代中经历了 N 次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。内存比新生代也大很多(大概比例是 1:2),当老年代内存满时触发 Full GC,Full GC 发生频率比较低,老年代对象存活时间比较长,存活率高。一般来说,大对象会被直接分配到老年代。所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组。当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和 JVM 的相关参数。

3、垃圾回收器

垃圾回收(GC)线程与应用线程保持相对独立,当系统需要执行垃圾回收任务时,先停止工作线程,然后命令 GC 线程工作。以串行模式工作的收集器,称为Serial Collector,即串行收集器;与之相对的是以并行模式工作的收集器,称为Paraller Collector,即并行收集器。

Serial 收集器
串行收集器采用单线程方式进行收集,且在 GC 线程工作时,系统不允许应用线程打扰。此时,应用程序进入暂停状态,即 Stop-the-world。Stop-the-world 暂停时间的长短,是衡量一款收集器性能高低的重要指标。Serial 是针对新生代的垃圾回收器,采用“复制”算法。

ParNew 收集器
并行收集器充分利用了多处理器的优势,采用多个 GC 线程并行收集。可想而知,多条 GC 线程执行显然比只使用一条 GC 线程执行的效率更高。一般来说,与串行收集器相比,在多处理器环境下工作的并行收集器能够极大地缩短 Stop-the-world 时间。ParNew 是针对新生代的垃圾回收器,采用“复制”算法,可以看成是 Serial 的多线程版本

Parallel Scavenge 收集器
Parallel Scavenge 是针对新生代的垃圾回收器,采用“复制”算法,和 ParNew 类似,但更注重吞吐率。在 ParNew 的基础上演化而来的 Parallel Scanvenge 收集器被誉为“吞吐量优先”收集器。吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。如虚拟机总运行了 100 分钟,其中垃圾收集花掉 1 分钟,那吞吐量就是99%。

Parallel Scanvenge 收集器在 ParNew 的基础上提供了一组参数,用于配置期望的收集时间或吞吐量,然后以此为目标进行收集。通过 VM 选项可以控制吞吐量的大致范围:

-XX:MaxGCPauseMills:期望收集时间上限,用来控制收集对应用程序停顿的影响。
-XX:GCTimeRatio:期望的 GC 时间占总时间的比例,用来控制吞吐量。
-XX:UseAdaptiveSizePolicy:自动分代大小调节策略。
但要注意停顿时间与吞吐量这两个目标是相悖的,降低停顿时间的同时也会引起吞吐的降低。因此需要将目标控制在一个合理的范围中。

Serial Old 收集器
Serial Old 是 Serial 收集器的老年代版本,单线程收集器,采用“标记-整理”算法。这个收集器的主要意义也是在于给 Client 模式下的虚拟机使用。

Parallel Old 收集器
Parallel Old 是 Parallel Scanvenge 收集器的老年代版本,多线程收集器,采用“标记-整理”算法。

CMS收集器
CMS(Concurrent Mark Swee)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS 收集器仅作用于老年代的收集,采用“标记-清除”算法,它的运作过程分为 4 个步骤:

初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
其中,初始标记、重新标记这两个步骤仍然需要 Stop-the-world。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始阶段稍长一些,但远比并发标记的时间短。

CMS 以流水线方式拆分了收集周期,将耗时长的操作单元保持与应用线程并发执行。只将那些必需 STW 才能执行的操作单元单独拎出来,控制这些单元在恰当的时机运行,并能保证仅需短暂的时间就可以完成。这样,在整个收集周期内,只有两次短暂的暂停(初始标记和重新标记),达到了近似并发的目的。

CMS收集器优点:并发收集,低停顿。
CMS收集器缺点:
· CMS 收集器对 CPU 资源非常敏感;
· CMS 收集器无法处理浮动垃圾;
· CMS 收集器是基于“标记-清除”算法,该算法的缺点都有。
· CMS 收集器之所以能够做到并发,根本原因在于采用基于“标记-清除”的算法并对算法过程进行了细粒度的分解。前面已经介绍过“标记-清除”算法将产生大量的内存碎片这对新生代来说是难以接受的,因此新生代的收集器并未提供 CMS 版本。
G1收集器
采用标记整理算法实现,运作过程:初始标记,并发标记,最终标记,筛选回收。不会产生大量空间碎片,可以精确控制停顿;G1将整个堆分为大小相等的多克Region ,G1跟踪每个区域的垃圾大小,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的区域,以达到在有限的时间获得最高的回收效率。
G1的GC模式分为两种:
· Yong GC:就是在新生代中 ,当所有 Eden 区域使用达到最大阀值并且无法申请足够内存时,会触发一次 YoungGC。每次 Young GC 会回收所有 Eden 以及 Survivor0 区的垃圾,并将存活对象复制到Survivor1区和Old区。
· Mix GC:这里引入Rset概念,什么是Rset呢?Rset是Region的一小部分区域,用于记录其他Region引用当前Region的记录。首先会进行初次标识,仅仅标记一下GC Roots 和RootRegion需要STW。第二步,会扫描整个Old区,看Old区的Region中的Rset是否别RootRegion引用,如果被引用就会对该Region进行标记;第三步,并发标记因为只需要遍历没有被Root Region标记的区域,所以范围缩小了;第四步重新标记,是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,但是G1不同于CMS采用了SATB速度是比CMS快的;第五步清理,采用了复制清理所以会STW。但是智慧选择垃圾量较多的Region进行清理,而不是全部清理,虽然垃圾清理不太干净,但是清理的时间会非常的短。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1032357.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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