JVM原理讲解和调优(二)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: JVM原理讲解和调优

3.4JVM分别对新生代和老年代采用不同的垃圾回收机制

3.4.1新生代的GC

新生代通常存活时间较短,因此基于复制算法来进行回收,所谓复制算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中.

对应于新生代:就是在Eden和其中一个Survivor,复制到另一个之间Survivor空间中,然后清理掉原来就是在Eden和其中一个Survivor中的对象。在执行机制上JVM提供了串行GC(Serial Old)、并行 回收 GC(parallel Old)和并发GC。

并发和并行的区别

新生代常见的垃圾收集器

1)串行GC(Serial GC)

   在整个扫描和复制过程采用单线程的方式来进行,主要用在嵌入式等内存比较小的程序,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定

2)并行回收GC(Parallel Scavenge)

   多条垃圾收集起并行工作,但是用户线程时阻塞的,适用于交互性比较弱的场景,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数。Parallel Scavenge(-XX:+UseParallelGC)框架下,默认是在要触发full GC前先执行一次young GC,并且两次GC之间能让应用程序稍微运行一小下,以期降低full GC的暂停时间(因为young GC会尽量清理了young gen的死对象,减少了full GC的工作量)

3)并发GC (ParNew)

用户线程和垃圾回收器并发执行,适用于响应时间优先,交互性比较强的场景,并发GC的触发条件就不太一样。以CMS GC (-XX:+UseConcMarkSweepGC -XX:+UseParNewGC)为例,它主要是定时去检查old gen的使用量,当使用量超过了触发比例就会启动一次CMS GC,对old gen做并发收集

3.4.2老年代的GC

  老年代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,CMS用标记清除,G1用标记整理。在执行机制上JVM提供了串行GC(Serial Old)、并行 回收 GC(parallel Old)和并发GC。[CMS (-XX:+UseConcMarkSweepGC -XX:+UseParNewGC), G1(-XX:UseG1GC)],具体算法细节还有待进一步深入研究。

老年代常见的垃圾收集器

3.4.2.1 Serial Old(标记整理算法)

3.4.2.2 Parallel Old(标记整理算法)

3.4.2.3 CMS 收集器

优点:并发收集、低停顿

缺点:

1)CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。

2)CMS收集器无法处理浮动垃圾,可能会出现“Concurrent Mode Failure(并发模式故障)”失败而导致Full GC产生。

3)CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

 

CMS过程:

初始标记(STW )在这个阶段,需要虚拟机停顿正在执行的任务,官方的叫法STW(Stop The Word)。这个过程从垃圾回收的"根对象"开始,只扫描到能够和"根对象"直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了。

并发标记 :这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。

重新标记(STW )这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从"根对象"开始向下追溯,并处理对象关联。

并发清理 :清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。

相关参数:

-XX:ConcGCThreads:并发的 GC 线程数

-XX:+UseCMSCompactAtFullCollection:FullGC 之后做压缩

-XX:CMSFullGCsBeforeCompaction:多少次 FullGC之后压缩一次

-XX:CMSInitiatingOccupancyFraction:触发 FullGC

-XX:+UseCMSInitiatingOccupancyOnly:是否动态可调

-XX:+CMSScavengeBeforeRemark:FullGC之前先做 YGC

-XX:+CMSClassUnloadingEnable:启用回收Perm 区

 

3.4.3 G1收集器(介于新生代和老年代之间的垃圾收集器,复制+标记整理算法)

G1 jdk8开始,推荐使用(重点学习,jdk9中已经设为默认垃圾收集器)

G1(global concurrent marking)的执行过程分为五个步骤:

初始标记(initial mark,STW)

在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。

并发标记(Concurrent Marking)

G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断

最终标记(Remark,STW)

该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。把Rember Set log的数据合并到Rember Set中

清除垃圾(Cleanup,STW)

 

G1具备如下特点:

1、并行于并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。

2、分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。

3、空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

4、可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,

 

G1的几个概念

Region

传统的GC收集器将连续的内存空间划分为新生代、老年代和永久代(JDK 8去除了永久代,引入了元空间Metaspace),这种划分的特点是各代的存储地址(逻辑地址,下同)是连续的。如下图所示:

而G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。如下图所示:

在上图中,我们注意到还有一些Region标明了H,它代表Humongous,这表示这些Region存储的是巨大对象(humongous object,H-obj),即大小大于等于region一半的对象

为了减少连续H-objs分配对GC的影响,需要把大对象变为普通的对象,建议增大Region size。

一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数。如果不设定,那么G1会根据Heap大小自动决定

SATB

Snapshot-At-The-Beginning,由字面理解,是GC开始时活着的对象的一个快照。它是通过Root Tracing得到的,作用是维持并发GC的正确性。

那么它是怎么维持并发GC的正确性的呢?根据三色标记算法,我们知道对象存在三种状态:

  • 白:对象没有被标记到,标记阶段结束后,会被当做垃圾回收掉。
  • 灰:对象被标记了,但是它的field还没有被标记或标记完。
  • 黑:对象被标记了,且它的所有field也被标记完了。

RSet

记录了其他 Region中的对象引用本 Region 中对象的关系,全称是Remembered Set,是辅助GC过程的一种结构,典型的空间换时间工具。逻辑上说每个Region都有一个RSet,RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象),而Card Table则是一种points-out(我引用了谁的对象)的结构

上图中有三个Region,每个Region被分成了多个Card,在不同Region中的Card会相互引用,Region1中的Card中的对象引用了Region2中的Card中的对象,蓝色实线表示的就是points-out的关系,而在Region2的RSet中,记录了Region1的Card,即红色虚线表示的关系,这就是points-into。

G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的

YoungGC

新对象进入 Eden 区

存活对象拷贝到Survivor 区

存活时间达到年龄阈值时,对象晋升到 Old 区

MixedGC

不是 FullGC,选定所有年轻代里的Region,外加根据global concurrent marking统计得出收益高的若干老年代Region

MixedGC时机

-XX:InitiatingHeapOccupancyPercent   堆占有率到达这个数值时触发global concurrent marking,默认45%

-XX:G1HeapWastePercent   在global concurrent marking可以知道区有多少空间可以被回收,YGC和Mixed GC之间判断垃圾占比是否到达此参数,到达了才会发生Mixed GC

-XX:G1MixedGCLiveThresholdPercent

-XX:G1MixedGCCountTarget

-XX:G1OldGCSetRegionThresholdPercent

-XX:+UseG1GC 开启 G1

-XX:G1HeapRegionSize=n, Region 的大小,1-32M,最多2048个

-XX:MaxGCPauseMillis=200 最大停顿时间

-XX:G1NewSizePercent、-XX:G1MaxNewSizePercent

-XX:G1ReservePercent=10 保留防止 to space溢出

-XX:ParallelGCThreads=n SWT线程数

-XX:ConcGCThreads=n 并发线程数=1/4*并行

需要切换到 G1的情况:

1. 50%以上的堆被存活对象占用

2. 对象分配和晋升的速度变化非常大

3. 垃圾回收时间特别长,超过了1秒

3.4.4 GC机制的组合适用

以上各种GC机制是需要组合使用的,指定方式由下表所示:

指定方式

新生代GC方式

旧生代GC方式

-XX:+UseSerialGC

串行GC

串行GC

-XX:+UseParallelGC

并行回收GC

并行GC

-XX:+UseConeMarkSweepGC

并行GC

并发GC

-XX:+UseParNewGC

并行GC

串行GC

-XX:+UseParallelOldGC

并行回收GC

并行GC

-XX:+ UseConeMarkSweepGC

-XX:+UseParNewGC

串行GC

并发GC

不支持的组合

1、-XX:+UseParNewGC -XX:+UseParallelOldGC

2、-XX:+UseParNewGC -XX:+UseSerialGC

标题

3.4.5  GC 日志的分析

打印日志相关参数:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
例(默认为 ParallelGC, 其它的添加-XX:+UseConcMarkSweepGC或-XX:+UseG1GC即可):
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log"

CMS日志格式(   -XX:+UseConcMarkSweepGC)

 

G1日志格式YCG

G1日志 global concurrent marking

G1日志 MixedGC

下面的文章是具体实战下的各种情况调优,虽然里面的日志格式不是最新的但是,但是大部分是可以复用的

CMS日志格式

G1日志格式

 

可视化工具分析GC日志

Universal JVM GC analyzer - Java Garbage collection log analysis made easy 在线工具

GCViewer   mvn clean package -Dmaven.test.skip 生成 jar包,双击执行,导入日志即可进入图形化分析页面

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
4天前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
15天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
23天前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
28天前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
30天前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。