JVM(七)——垃圾收集器的补充说明

前言

之前总结过各种垃圾收集器,但是没有针对如何选择垃圾收集器,并没有做一个合适的介绍,这篇博客会在之前博客的基础上,先总结所有垃圾收集器,然后再总结一下垃圾收集器的选择策略,之前博客的链接地址——JVM——垃圾收集器

GC Root

之前已经总结过一些垃圾回收算法,但是哪些对象能做为GC Root对象之前只是在博客中简单总结了一笔,但是没探讨原因,前两篇博客的总结,其实是顺便将JVM的各个内存模型和布局都梳理了一遍,重要的是这个图。
在这里插入图片描述
我们这里探讨的GC Root其实是通俗点解释就是有资格以上帝视角连接堆中对象的实例。所以能称为上帝视角的实例对象,首先满足的一个条件就是——不能在堆中(如果在堆中,还有啥上帝视角可言?)所以从上图中来看,首先最有可能成为GC Root对象的就是虚拟机栈中的本地变量。同样的本地方法栈中的变量也可能成为GC Root
我们之前总结的时候,还有一张图
在这里插入图片描述

这个方法区中会分配静态变量,静态变量会分配在方法区,然后指向堆中指定的对象,并不会随着堆被创建。静态变量完全可以有上帝视角,因此第二种可能成为GC Root的就是静态变量

此外还有类加载器和Thread线程类,因为这两者生命周期较长,同时也在堆之外,完全具备上帝视角。

总结一下,可能成为GC Root的对象为:

1、虚拟机栈中的引用对象

2、方法区中的静态属性引用的对象和常量引用对象。

3、本地方法栈中的JNI引用对象

4、类加载器和活着的Thread对象

这些不就是《深入理解Java虚拟机》一书中所提到过的么?不用刻意去记忆,只需要从所谓的上帝视角切入,就能理解为啥这些对象能作为GC Root的对象。

垃圾回收算法

这个之前总结过,博客地址:对象与垃圾回收策略
总的来说有标记-清除(Mark-Sweep)、复制、标记整理(Mark-Compact)三种大类,但是针对新生代和老年代对象生命周期不同的特点,JVM在设计的时候就采用了分代的思想,即不懂的分代采用的算法不同,但也没逃出上述三种的思想描述。总的来讲
Young区:采用复制算法(因为新生代的对象生命周期一般比较短,为了一定程度上减少内存碎片,Young区采用复制的效率比较高,因为存活的对象比较少,复制也没多大压力)
Old区:采用标记清理算法(因为老年代的对象生命周期比较长,没必要复制对内存空间进行清理,只需做个标记即可)

垃圾收集器

之前针对垃圾收集器也做过总结,如下图所示。
img
这里在之前博客的基础上要补充的是,从上图中可以看出Serial,ParNew和ParallelGC是新生代的垃圾收集器,之前也总结了,新生代垃圾收集器采用复制算法似乎更合理,因此上述三者其实采用的就是复制算法来进行垃圾回收。
这里需要补充说明一下ParNew和ParallelGC两者的区别
ParNew其实就是Serial的多线程版本,ParallelGC其实也是一个多线程版本的垃圾收集器但是ParallelGC的关注点不一样,**ParallelGC更加关注的是吞吐量。**吞吐量=用户线程执行耗时/(用户线程执行耗时+垃圾收集线程执行耗时)。例如:用户线程执行99ms,垃圾收集时间耗费1ms,则系统吞吐量是99%。
之前也说道,Old区采用标记整理的算法比较合适,因此上图中在老年代的三个垃圾收集器是针对标记整理算法的实现。

并发与并行:
在JVM里面,可以看到并发与并行的一个区别,并发:用户和垃圾线程同步进行。并行:有多个线程同时进行垃圾收集的操作

CMS

这里依旧补充说明一下CMS垃圾收集器,这个收集器的重点关注在停顿时间上面,(其实衡量一个垃圾收集器需要从两个维度来衡量,一个是吞吐量,一个就是停顿响应时间)。
官网中关于CMS的介绍——官网关于CMS的导读
The Concurrent Mark Sweep (CMS) collector is designed for applications that prefer shorter garbage collection pauses and that can afford to share processor resources with the garbage collector while the application is running.
官网中有一段这样的描述,可以很明确的知道,CMS是为了追求更短的响应时间。

G1

G1垃圾收集器,可以由用户设定响应时间,适用于新生代也适用于老年代。官网有如下描 ——Oracle G1垃圾收集器,其中有如下一段话:It attempts to meet garbage collection (GC) pause time goals with high probability while achieving high throughput. G1垃圾收集器是为了提高吞吐量并缩短响应时间而产生的,其整个流程如下所示。
在这里插入图片描述
这里需要说一下其中的筛选回收阶段,筛选回收阶段其实是G1选择性回收的阶段,会根据用户设置的响应时间清理垃圾对象最多的几个区域,如果用户设置的响应时间过短,则G1清理的区域会相对较少,如果用户设置的响应时间较长,则G1清理的区域会相对较多,清理的顺序按照垃圾对象的比例从大到小进行清理,这就是为什么成为G1(Garbage First)的原因。
什么样的情况下用G1?
Applications running today with either the CMS or the with parallel compaction would benefit from switching to G1 if the application has one or more of the following traits.
More than 50% of the Java heap is occupied with live data.

The rate of object allocation rate or promotion varies significantly.

The application is experiencing undesired long garbage collection or compaction pauses (longer than 0.5 to 1 second).
官网中的这样一段话提供了参考:1、堆内存使用超过50%。2、对象分配和存活率较高。3、期望垃圾收集的停顿相应时间较短。可以从这三个角度去参考是否使用G1垃圾回收器。

如何选择垃圾收集器

这一点需要重要说明,其实我们参考了很多国内的资料,其实关于垃圾收集器在Oracle的官网中早就已经进行了说明——Oracle Selecting a Collector 下面一段描述的非常清晰了

Unless your application has rather strict pause time requirements, first run your application and allow the VM to select a collector. If necessary, adjust the heap size to improve performance. If the performance still does not meet your goals, then use the following guidelines as a starting point for selecting a collector.

If the application has a small data set (up to approximately 100 MB), thenselect the serial collector with the option -XX:+UseSerialGC.

If the application will be run on a single processor and there are no pause time requirements, then let the VM select the collector, or select the serial collector with the option ``-XX:+UseSerialGC.

If (a) peak application performance is the first priority and (b) there are no pause time requirements or pauses of 1 second or longer are acceptable, then let the VM select the collector, or select the parallel collector with ``-XX:+UseParallelGC.

If response time is more important than overall throughput and garbage collection pauses must be kept shorter than approximately 1 second, then select the concurrent collector with ``-XX:+UseConcMarkSweepGC or-XX:+UseG1GC.

除非我们的程序对停顿时间有着很高的要求,否则优先让虚拟机自己去进行选择,如果JVM优先选择的垃圾收集器无法满足我们的要求,我们就可以通过调整堆的大小来提高性能,如果还不能满足要求,就按照以下的知道进行垃圾收集器的选择

1、如果应用程序的数据集合比较小(顶天了100M)则选择串行化收集器就行了(Serial垃圾收集器)

2、如果应用程序在单核上运行,并且没有停顿时间的要求,则让JVM自主选择或者使用Serial垃圾收集器

3、如果应用程序重点在意的是吞吐量,没有停顿时间的要求,或者停顿1秒或者更长你们忍受,那么可以用ParallelGC垃圾收集器

4、如果应用程序更加在意的是响应时间而不是吞吐量,同时要求垃圾收集的实际响应近乎要小于1秒,则最好选择CMS垃圾收集器和G1收集器。

这些,官网早就描述的非常清楚了。

总结

本篇博客只是对之前的垃圾收集器进行了一个总结,所谓的JVM调优,相当一部分是针对垃圾收集器的停顿时间和吞吐量进行。后续会对一些工具的使用进行总结。

发布了129 篇原创文章 · 获赞 37 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/liman65727/article/details/103844598