JVM之垃圾收集器

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 JVM之垃圾收集器,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

垃圾收集器

概述

垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法)的具体实现,不同商家、不同版本的JVM所提供的垃圾收集器可能会有很大差别

Java垃圾回收器是Java虚拟机(JVM)的三个重要模块之一,另外两个是解释器和多线程机制,为应用程序提供内存的自动分配(Memory Allocation)、自动回收(Garbage Collect)功能,这两个操作都发生在Java堆上

某一个时点,一个对象如果有一个以上的引用(Rreference)指向它,那么该对象就为活着的(Live),否则死亡(Dead),视为垃圾,可被垃圾回收器回收再利用。

垃圾回收操作需要消耗CPU、线程、时间等资源,所以容易理解的是垃圾回收操作不是实时的发生(对象死亡马上释放),当内存消耗完或者是达到某一个指标(Threshold,使用内存占总内存的比列,比如0.75)时,触发垃圾回收操作。

有一个对象死亡的例外,java.lang.Thread类型的对象即使没有引用,只要线程还在运行,就不会被回收。

官网文档参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html

收集器的分类

串行垃圾收集器Serial、Serial Old

只能有一个垃圾回收线程执行,用户线程暂停

适用于内存比较小的嵌入式设备

并行垃圾收集器:ParNew、Parallel Old、Paraller Scavenge

并行收集器要求吞吐量优先

吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集时间)

高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态

适用于计算、后台处理等若交互场景

并发垃圾收集器:CMS、G1

并发收集器要求:停顿时间优先

停顿时间:垃圾收集器进行垃圾回收终端应用执行响应的时间

停顿时间越短就越适合需要和用户交互的程序,良好的响应速度能提升用户体验

用户线程和垃圾收集线程同时执行(不一定是并行,可能是交替执行),垃圾收集线程在执行的时候不会停顿用户线程的运行

适用于相对时间有要求的场景

在这里插入图片描述

查看默认收集器

jvm分client 和 server模式。如果启动jvm不指定模式,jdk会根据当前的操作系统配置来启动不同模式的jvm。默认64bit操作系统下都会是server模式的jvm。

C:\Users\Administrator>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266527104 -XX:MaxHeapSize=4264433664 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

使用命令行指令: jinfo -flag 相关垃圾回收器参数 进程ID

jinfo -flag UseG1GC 12345

串行垃圾收集器

单线程执行回收操作,回收期间暂停所有应用线程的执行,通过-XX:+UseSerialGC命令指定

该参数同时指定年轻代和老年代都使用串行收集器,年轻代使用:Serial收集器,老年代使用:Serial Old收集器。

Serial收集器采用复制算法

Serial Old收集器使用标记一压缩算法。

Serial

Serial收集器是最基本、发展历史最悠久的收集器,曾经在JDK1.3.1之前是虚拟机新生代收集的唯一选择。

它是一种单线程收集器,意味着它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在进行垃圾收集的时候需要暂停其他线程。

简单高效,拥有很高的单线程收集效率

收集过程需要暂停所有线程

使用复制算法

适用于新生代

Client模式下的默认新生代收集器

Serial Old

Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用标记-整理算法,运行过程Serial收集器一样。

参数设置

-XX:+UseSerialGC

-XX:+UseSerialOldGC

并行垃圾收集器

ParNew

并行回收器在串行回收器基础上做了改进,可以使用多个线程同时进行垃圾回收,计算能力强的计算机可以有效的缩短垃圾回收所需的实际时间。

ParNew回收器是一个工作在新生代的垃圾收集器,只是简单的将串行回收器多线程化,它的回收策略和算法和串行回收器一样

可以把ParNew这个收集器理解为Serial收集器的多线程版本

在多CPU时,比Serial效率高

收集过程暂停所有应用程序线程,单CPU时比Serial效率差

使用复制算法

适用于新生代

运行在Server模式下的虚拟机中首选的新生代收集器	

Parallel Scavenge

Parallel GC代表新生代默认使用的是Paraller Scavenge进行垃圾收集的,而老年代则使用是Parallel Old进行垃圾收集

新生代的Parallel GC回收器,它是使用复制算法的收集器。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量,它也被称为吞吐量优先的垃圾收集器。

若吞吐量越大,意味垃圾收集的时间越短,则代码可以充分利用CPU资源,尽快完成程序运算任务

吞吐量计算公式:

吞吐量 = 运行代码时间 / (运行代码时间+垃圾收集时间)

例如:虚拟机总共运行100分钟,垃圾收集时间用了1分钟,则:吞吐量=(100-1)/100=99%

直接JVM参数设置

-XX:MaxGCPauseMillis  控制最大的垃圾收集停顿时间

-XX:GCTimeRatio  直接设置吞吐量的大小

Parallel Old

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法进行垃圾回收,也是更加关注系统的吞吐量。

参数设置

-XX:+UseParNewGC 设置年轻代使用ParNew并行回收器执行内存回收任务

-XX:+UseParallelGC 设置年轻代使用Paraller Scavenge并行回收器执行内存回收任务

-XX:+UseParallelOldGC 指定老年代使用Parallel Old并行回收收集器

-XX:+ParallelGCThreads 设置年轻代并行收集器的线程数

并发垃圾收集器

CMS

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。

特点:

并发收集、低停顿

产生大量空间碎片、并发阶段会降低吞吐量,还会并发失败

CMS收集器是基于标记-清除算法实现的,整个收集过程大致分为4个步骤:

初始标记 	CMS initial mark 	标记GC Roots直接关联对象,不用Tracing,速度很快

并发标记 	CMS concurrent mark 	进行GC Roots Tracing

重新标记 	CMS remark 		修改并发标记因用户程序变动的内容

并发清除 	CMS concurrent sweep	 清除不可达对象回收空间,同时有新垃圾产生,留着下次清理称为浮动垃圾

由于整个过程中,并发标记和并发清除,收集器线程可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。

在这里插入图片描述

参数设置

-XX:+UseConcMarkSweepGC  	开启CMS垃圾收集器

-XX:+UseCMSCompactAtFullCollection 	Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长。默认开启,与-XX:CMSFullGCsBeforeCompaction配合使用

-XX:+CMSFullGCsBeforeCompaction=0  设置进行几次Full GC后,进行一次碎片整理,默认0

-XX:ParallelCMSThreads  设定CMS的线程数量(一般情况约等于可用CPU数量) 

-XX:+UseCMSInitiatingOccupancyOnly 	辅助CMSInitiatingOccupancyFraction参数,不然CMSInitiatingOccupancyFraction只会使用一次就恢复自动调整,也就是开启手动调整

-XX:CMSInitiatingOccupancyFraction=-1 默认-1,取值0-100,按百分比回收

注意:CMS并发GC不是full GC。HotSpot VM里对concurrent collection和full collection有明确的区分。所有带有FullCollection字样的VM参数都是跟真正的full GC相关,而跟CMS并发GC无关的。

G1垃圾收集器

G1回收器(Garbage-First)是为了取代CMS回收器,G1回收器拥有独特的垃圾回收策略,所谓Garbage-Frist,其实就是优先回收垃圾最多的Region区域

G1垃圾收集器相对比其他收集器而言,Java堆的内存布局与就与其他收集器有很大差别,最大的区别在于:虽然保留新生代和老年代的概念,但新生代和老年代不再是物理隔离的了

也就是说取消了年轻代、老年代的物理划分,取而代之的是将堆划分为多个大小相等的独立若干个区域(Region),这些区域(不需要连续)中包含了有逻辑上的年轻代、老年代区域。

G1在JDK 7开始使用,JDK 8非常成熟,JDK 9默认的垃圾收集器,适用于新老生代。

特性

G1不需要其他收集器配合就能独立管理整个GC堆,但仍保留分代的概念,使用分代收集

在空间整合上,整体看属于标记-整理算法,局部看属于复制算法实现,不会导致空间碎片

可预测停顿时间:即让使用者明确指定在一个长度为M毫秒的时间片段(通过参数-XX:MaxGCPauseMillis)内完成垃圾收集,也就是消耗在垃圾收集上的时间不得超过N毫秒

Region

G1默认将Java堆划分成约2048个大小相同的独立的Region块,每一个Region块大小根据堆空间的实际大小来决定,范围控制在1MB到32MB之内

每个Region大小都是一样的,可以是1M到32M之间的数值,但是必须保证是2的n次幂

在JVM生命周期内不会改变,除非JVM停止之后,重新设置Region的大小,否则region的大小不会改变

如果对象太大,一个Region放不下[超过Region大小的50%],那么就会直接放到Humongous区

设置Region大小:-XX:G1HeapRegionSize=M

Region使新生代和老年代的物理空间可以是不连续的

G1堆示意图

堆区被划分成了各个Region,各个Region分别表示Eden区,Survivor区,Old区。一个Region只能属于一个角色,也就是说一个Region不能一部分是Eden区,一部分是Old区或Survivor区。

G1垃圾收集器新增加了一种内存区域Humongous区。主要用于存储大对象,如果对象大小超过0.5个Region,就放到H

对于堆中大对象,默认会被分配到老年代,如果它是一个短期存在的对象,由于老年代垃圾收集的频率较低,这个对象是不能及时被回收掉的,会对垃圾收集造成负面的影响。因此:设置H区,就能够及时回收。如果一个H区装不下一个大对象,则寻找连续的H区来存储,如果找不到连续的H区,就会启动Full GC。

在这里插入图片描述

G1垃圾收集过程

G1在进行垃圾收集的时候,会根据每个Region预计垃圾收集所需时间与预计回收内存大小的占比来选择对哪些区域进行回收,也就是不再有Minor GC/Yong GC和Major GC/Full GC的概念,而是采用一种Mixed GC的方式,即混合回收的GC方式。

可分为以下步骤:

初始标记(Initial Marking)

标记以下GC Roots能够关联的对象,并且修改TAMS的值,需要暂停用户线程

并发标记(Concurrent Marking)

从GC Roots进行可达性分析,找出存活的对象,与用户线程并发执行

最终标记(Final Marking)

修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需暂停用户线程

筛选回收(Live Data Counting and Evacuation)

对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划

参数设置

-XXX:+UseG1GC 使用G1垃圾收集器

-XX:ParallelGCThreads 设置并行回收的线程数量

-XX: G1HeapReginSize 设置每个Region的大小,是2的幂次,1MB-32MB之间

-XX:MaxGCPauseMillis 最大停顿时间

-XX:ConcGCThreads 并发标记的线程数

-XX:InitiatingHeapOcccupancyPercent 默认45%,代表GC堆占用达到多少的时候开始垃圾收集

ZGC垃圾收集器

JDK11新引入的ZGC收集器,不管是物理上还是逻辑上,ZGC中已经不存在新老年代的概念,会分为一个个page,当进行GC操作时会对page进行压缩,因此没有碎片问题,只能在64位的linux上使用,目前用得比较少

可以达到10ms以内的停顿时间要求,即使堆内存变大后停顿时间还是在10ms以内

支持TB级别的内存

垃圾收集器的选择

参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref28

优先调整堆大小然后让 VM 自己选择

内存小于100M,使用串行收集器:-XX:+UseSerialGC。

应用程序在单个处理器上运行并且没有暂停时间要求,则让 VM 选择收集器,或者选择带有选项的串行收集器:-XX:+UseSerialGC

允许停顿时间超过1秒,让 VM 选择收集器,或者选择并行收集器:-XX:+UseParallelGC

响应时间比整体吞吐量更重要,并且垃圾收集暂停必须保持短于大约 1 秒,则使用-XX:+UseConcMarkSweepGC或选择并发收集器-XX:+UseG1GC

常用命令参数

参数 描述
-Xms Initial heap memory size 初始化heap大小
-Xmx Maximum heap memory size 设置最大的heap大小
-Xmn Size of Young Generation 年轻代的大小
-XX:PermSize Initial Permanent Generation size 初始化永久带的大小
-XX:MaxPermSize Maximum Permanent Generation size 最大的永久带大小
-XX:+UseSerialGC Serial Garbage Collector 串行垃圾回收器
-XX:+UseParallelGC Parallel Garbage Collector并行垃圾回收器
-XX:+UseConcMarkSweepGC CMS Garbage Collector并发标记垃圾回收器
-XX:+UseG1GC G1 Gargbage Collector 使用G1垃圾回收器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136872.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!