JAVA GC 分代垃圾收集过程及各种GC的特点_java series gc-程序员宅基地

技术标签: JAVA基础  

1 JVM性能相关的组件

在调优性能时,JVM的如下三个组件是重点关注的:

  • 堆:存储对象数据的地方,该区域由启动时选择的垃圾收集器管理。大多数调优选项都与堆大小设置与选择的垃圾回收器相关
  • JIT编译器:对性能也有很大的影响,但是很少需要对新版本的JVM进行调优。
  • GC【垃圾回收器】:需要根据业务,选择最适合的GC

在这里插入图片描述

2 分代垃圾收集

2.1 垃圾收集算法

常见的垃圾收集算法大致分为以下两种:引用计数法和可达性分析算法,目前虚拟机都采用可达性算法

引用计数法

基本思路:每一个对象上记录这个对象被引用的次数,只要有任何一个对象引用了次对象,这个对象的计数器就+1,取消对这个对象的引用时,计数器就-1。任何一个时刻,如果该对象的计数器为0,那么这个对象就是可以回收的。但是这种算法有个弊端:无法处理循环引用的情况,见下图

在这里插入图片描述
在栈上,在method中执行完两个set后,method方法结束,图中两条红线引用消失,可以看到留下两个对象在堆内存中循环引用,但此时已经没有地方在用他们了,造成内存泄漏。两个对象不能被GC回收

class A {
    
    private B b;
    public void setB(B b) {
    
        this.b = b;
    }
}

class B {
    
    private A a = new A();
    public void setA(A a) {
    
        this.a = a;
    }
}

public void method() {
    
    A a = new A();
    B b = new B();
    a.setB(b);
    b.setA(a);
}

可达性分析算法

在Java中,是通过可达性分析(Reachability Analysis)来判定对象是否存活的。
该算法的基本思路就是通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。
常见的GC Roots有如下几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
- 被同步锁持有的对象。

在这里插入图片描述
从图上看出,对象实例1、2、4、6都具有GC Roots可达性,即存活对象,不能被GC回收。
而对于对象实例3、5虽然相互引用,但没有任何一个GC Roots与之相连,属于GC Roots不可达对象,即GC需要回收的垃圾对象。

2.2 分代垃圾收集过程

  1. 任何新对象都被分配到eden空间。两个survivor空间在启动时都是空的。
    在这里插入图片描述
  2. 当eden空间填满时,会触发一个小的垃圾收集 Young GC
    在这里插入图片描述
  3. 被引用的对象被移动到第一个survivor空间。清除eden空间后,未被引用的对象将被删除。
    在这里插入图片描述
  4. 在下一个 minor GC中,伊甸园空间也发生了同样的事情。未引用的对象被删除,引用的对象被移动到幸存者空间。但是它们被移动到第二个幸存者空间(S1)。此外,来自第一个survivor空间(S0)的上一次minor GC的对象的年龄增加并移动到S1。一旦所有幸存的对象被移动到S1, S0和eden都将被清除。注意现在在幸存者空间中有不同年龄的对象。
    在这里插入图片描述
  5. 在下一个minor GC中,重复相同的过程。然而,这一次幸存者空间切换。引用的对象被移动到S0。保存下来的对象年龄增加。eden和S1被清除。
    在这里插入图片描述
  6. 在一次minor GC之后,当老化对象达到某个年龄阈值(如8)时,它们将从年轻代提升到年老代。
    可通过如下2个参数控制对象何时提升到老年代:
    • -XX:PreternureSizeThreshold 直接晋升老年代的对象大小,设置了这个参数后,大于这个参数的对象直接在老年代进行分配。
    • -XX:MaxTenuringThreshold 晋升老年代的对象年龄,对象在每一次Minor GC后年龄增加一岁,超过这个值后进入到老年代。默认值是15。
      在这里插入图片描述
  7. 随着minor gc继续发生,对象将继续被提升到老的代空间。
    在这里插入图片描述
  8. 这几乎涵盖了年轻一代的整个过程。最后,将对老的代执行一次大的GC,以清除和压缩该空间。
    在这里插入图片描述

2.3 minor GC与major GC

在这里插入图片描述

3 垃圾回收器

3.1 Serial GC

Serial GC是JAVA 5、6中默认的GC,minor gc和 major gc都是串行完成的(使用单个虚拟CPU)。
Serial GC使用标记-压缩收集方法,此方法将旧内存移到堆的开头,以便在堆的末尾将新内存分配到单个连续内存块中。这种内存压缩使得向堆分配新内存块的速度更快。
Serial GC是最基本、历史最悠久的收集器,它是一个单线程收集器。一旦回收器开始运行时,整个系统都要停止。Client模式下默认开启,其他模式默认关闭。

在这里插入图片描述
用法:
-XX:+UseSerialGC
这个参数会使新生代和老年代都使用串行回收器,新生代使用复制算法,老年代使用标记-整理算法。

-XX:+UseParNewGC
ParNew收集器是Serial收集器的多线程版本,在新生代进行并行回收,老年代仍旧使用串行回收。新生代S区任然使用复制算法。
操作系统是多核CPU上效果明显,单核CPU建议使用串行回收器。打印GC详情时ParNew标识着使用了ParNewGC回收器。默认关闭。

3.2 Parallel GC

Parallel GC使用多个线程执行年轻代垃圾收集,是JVM中GC的默认实现,也称为吞吐量收集器。
并行收集器适用于在多处理器或多线程硬件上运行的具有中型到大型数据集的应用程序。

运行并行GC还会导致“停止世界事件”,并导致应用程序冻结,当需要完成大量工作且可以接受长时间暂停时,可以使用它,例如运行批处理作业。
在这里插入图片描述
吞吐量:CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机运行100分钟,垃圾收集花费1分钟,那吞吐量就99%

默认情况下,在具有N个cpu的主机上,并行垃圾收集器在收集中使用N个垃圾收集器线程。垃圾收集器线程的数量可以通过命令行选项来控制: -XX:ParallelGCThreads= = <所需数量>

-XX:+UseParallelGC

表示新生代使用Parallel收集器,并提供如下参数用于控制吞吐量:

  • -XX:MaxGCPauseMillis:设置最大停顿时间,值是一个大于0的毫秒数,收集器将尽力保证垃圾回收时间不超过设定值,系统运行的需要回收的垃圾总量是固定的,缩短停顿时间的同时会增大回收频度。
  • -XX:GCTimeRatio:用户控制垃圾回收时间占比,它运行的参数值是0-100的整数,如果参数设置为19,代表最大GC时间占总时间的5%(1/(1+19))。
  • -XX:UseAdaptiveSizePolicy:JVM会根据实际运行情况动态调整新生代大小、新生代和s区比例、晋升老年代对象大小等细节参数。

-XX:+UseParallelOldGC

新生代和老年代都使用并行收集器。打印出的GC会带PSYoungGen、ParOldGen关键字。
HotSpot只在老一代中进行压缩。HotSpot中的年轻一代被认为是一个拷贝收集器;因此不需要压缩。
这是自Java 7u4以来并行GC的默认版本

Compacting describes the act of moving objects in a way that there are no holes between objects. After a garbage collection sweep, there may be holes left between live objects. Compacting moves objects so that there are no remaining holes. It is possible that a garbage collector be a non-compacting collector. Therefore, the difference between a parallel collector and a parallel compacting collector could be the latter compacts the space after a garbage collection sweep. The former would not.

3.3 CMS

Concurrent Mark Sweep 并发标记清除,即使用CMS收集器 ConcMarkSweepGC
它试图通过与应用程序线程并发执行大部分垃圾收集工作来最小化由于垃圾收集而导致的暂停。通常并发低暂停收集器不会复制或压缩活动对象。垃圾收集是在不移动活动对象的情况下完成的。如果碎片成为一个问题,分配一个更大的堆。

它使用的是 标记清除算法,运作过程为四个步骤,分别是 初始标记—并发标识—重新标记—并发清除。它是老年代的收集算法,新生代使用ParNew收集算法。默认关闭

CMS的缺点对服务器CPU资源较为敏感,在并发标记时会降低吞吐量。它使用的标记清除算法也会产生大量空间碎片,空间碎片的存在会加大Full GC的频率,虽然老年代还有足够的内存,但是因为内存空间连续,不得不进行Full GC。

CMS收集器比其他gc使用更多的CPU。如果可以分配更多的CPU以获得更好的性能,那么CMS垃圾收集器是比并行收集器更好的选择。CMS GC中不执行压缩。
在这里插入图片描述

通过 -XX:+UseConcMarkSweepGC 设置使用CMS后,可以通过以下参数调整CMS的行为:

  • -XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次整理,整理过程是独占的,会引起停顿时间变长。仅在使用CMS收集器时生效。
  • -XX:ParallelCMSThreads 设置并行GC时进行内存回收的线程数量

3.4 G1

Java 7中提供,用于替换CMS收集器。G1是一种并行、并发、递增压缩的低暂停垃圾收集器,它的布局与前面描述的其他垃圾收集器截然不同。

它像CMS一样是并行和并发的,但是它的工作原理与较老的垃圾收集器非常不同。

虽然G1也分代,但它没有针对年轻代、老年代的单独区域。相反,每个代都是一组区域,允许以灵活的方式调整年轻代的大小。

它将堆划分为一组大小相等的区域(1MB到32MB—取决于堆的大小),并使用多个线程扫描它们。在程序运行期间,一个区域可以是一个老区域,也可以是一个年轻区域。

标记阶段完成后,G1知道哪些区域包含最多的垃圾对象。如果用户对最短的暂停时间感兴趣,G1可以选择只疏散几个区域。如果用户不担心暂停时间,或者已经声明了一个相当大的暂停时间目标,G1可能会选择包含更多区域。

由于G1GC识别出垃圾最多的区域,并首先对该区域执行垃圾收集,因此它被称为Garbage First。
在这里插入图片描述
除了Eden, Survivor和Old内存区域,G1GC中还有另外两种类型的区域:

  • Humongous—用于大尺寸对象(大于堆大小的50%)
  • 可用空间-未使用或未分配的空间

使用G1垃圾收集器的JVM参数是**-XX:+UseG1GC**

3.5 Epsilon GC

Epsilon是一个不做任何事情(no-op)的垃圾收集器,作为JDK 11的一部分发布。它处理内存分配,但不实现任何实际的内存回收机制。一旦可用的Java堆耗尽,JVM就会关闭。

它可以用于超延迟敏感的应用程序,开发人员确切地知道应用程序的内存占用,甚至(几乎)拥有完全没有垃圾的应用程序。否则,不建议在任何其他场景中使用Epsilon GC。

使用Epsilon垃圾收集器的JVM参数是**-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC**.

3.6 Shenandoah GC

Shenandoah是作为JDK 12的一部分发布的一个新的GC。Shenandoah相对于G1的关键优势是,它与应用程序线程并发执行更多的垃圾收集周期工作。G1只能在应用程序暂停时清空它的堆区域,而Shenandoah可以与应用程序同时重新定位对象。

Shenandoah可以压缩活动对象,清除垃圾,并在检测到空闲内存后几乎立即将RAM释放回操作系统。由于所有这些都是在应用程序运行时并发发生的,Shenandoah是CPU密集型的

使用Epsilon垃圾收集器的JVM参数是:-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

3.7 ZGC

ZGC是作为JDK 11的一部分发布的另一个GC,在JDK 12中得到了改进。它适用于需要低延迟(少于10毫秒的暂停)和/或使用非常大的堆(几万亿字节)的应用程序。

ZGC的主要目标是低延迟、可伸缩性和易用性。为了实现这一点,ZGC允许Java应用程序在执行所有垃圾收集操作时继续运行。默认情况下,ZGC未提交未使用的内存并将其返回给操作系统。

因此,通过提供极低的暂停时间(通常在2ms内),ZGC比其他传统gc带来了显著的改进。

Shenandoah和ZGC都计划成为产品特性,并在JDK 15中走出实验阶段

4 如何选择GC

如果应用程序没有严格的暂停时间要求,你应该只运行你的应用程序并允许JVM选择正确的收集器。
大多数情况下,默认设置应该可以正常工作。如果有必要,可以调整堆大小以提高性能。如果性能仍然不能满足您的目标,您可以根据应用程序的需求修改收集器:

GC 适用场景
Serial 如果应用程序有一个小的数据集(大约100 MB)和/或它将运行在没有暂停时间要求的单个处理器上
Parallel 如果峰值应用程序性能是优先级,并且没有暂停时间要求,或者一秒或更长时间的暂停是可以接受的
CMS/G1 如果响应时间比总体吞吐量更重要,那么垃圾收集暂停时间必须短于大约一秒
ZGC 如果响应时间具有高优先级,并且/或您正在使用一个非常大的堆

5. 配置参数

  • 获取到完整的机器名 -Djava.net.preferIPv4Stack=true
  • 打印GC日志 -Xloggc:/var/log/hive/hiveserver2-gc-%t.log -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M
  • 打印OOM日志 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/hive/hs2_heapdump.hprof

参考

Java Garbage Collection Basics
Garbage Collection in Java – What is GC and How it Works in the JVM

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/penriver/article/details/118944545

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签