JVM GC原理及调优的基本思路
若观察到Tomcat进程CPU使用率较高,并在GC日志中发现GC次数比较频繁、GC停顿时间长,说明需优化GC。
CMS和G1是时下使用率比较高的两款垃圾收集器,从Java 9开始,G1是默认垃圾收集器。
CMS vs G1
CMS收集器
将Java堆分为新生代(Young)或老年代(Old),因为研究表明,超过90%的对象在第一次GC时就被回收掉,仅少数对象会存活较长。
CMS还将新生代内存空间分为幸存者空间(Survivor)和伊甸园空间(Eden):
- 新的对象始终在Eden空间上创建
- 一旦一个对象在一次垃圾收集后还幸存,就会被移动到幸存者空间
当一个对象在多次垃圾收集之后还存活时,它会移动到年老代。这样做的目的是在年轻代和年老代采用不同的收集算法,以达到较高的收集效率,比如在年轻代采用复制-整理算法,在年老代采用标记-清理算法。
G1收集器
与CMS相比,G1收集器有两大特点:
- G1可以并发完成大部分GC的工作,这期间不会“Stop-The-World”
- G1使用非连续空间,这使G1能够有效地处理非常大的堆。此外,G1可以同时收集年轻代和年老代。G1将堆分成许多(通常几百个)小区域。这些区域固定大小(默认大约2M)。每个区域都分配给一个空间。
U表示“未分配”区域。G1将堆拆分成小的区域:可以做局部垃圾回收,而无需每次都回收整个区域,这样回收的停顿时间会比较短。
收集过程
- 将所有存活的对象将从收集的区域复制到未分配的区域,比如收集的区域是Eden空间,把Eden中的存活对象复制到未分配区域,这个未分配区域就成了Survivor空间。理想情况下,如果一个区域全是垃圾(意味着一个存活的对象都没有),则可以直接将该区域声明为“未分配”
- 为了优化收集时间,G1总是优先选择垃圾最多的区域,从而最大限度地减少后续分配和释放堆空间所需的工作量。这也是G1收集器名字的由来——Garbage-First。
GC调优原则
GC有代价,因此根本原则是每次GC都回收尽可能多的对象。
针对CMS和G1有相应策略。
CMS收集器
最重要的是合理地设置年轻代和年老代大小。
- 年轻代太小,会导致频繁Minor GC,并且很有可能存活期短的对象也不能被回收,GC的效率就不高
- 年老代太小,容纳不下从年轻代过来的新对象,会频繁触发单线程Full GC,导致较长时间的GC暂停,影响Web应用的响应时间。
G1收集器
不推荐直接设置年轻代大小,和CMS不不同,因为G1会根据算法动态决定年轻代和年老代大小。
因此对于G1,最关心Java堆总大小(-Xmx)。
-XX:MaxGCPauseMillis = n
限制最大GC暂停时间,以尽量不影响请求的响应时间。G1将根据先前收集信息及检测到的垃圾量,估计它可以立即收集的最大区域数量,从而尽量保证GC时间不会超出这个限制。因此G1更“智能”,使用更简单。
内存调优实战
下面我通过一个例子实战一下Java堆设置得过小,导致频繁的GC,我们将通过GC日志分析工具来观察GC活动并定位问题。
1.首先我们建立一个Spring Boot程序,作为我们的调优对象
@RestController
public class GcTestController {private Queue<Greeting> objCache = new ConcurrentLinkedDeque<>();@RequestMapping("/greeting")public Greeting greeting() {Greeting greeting = new Greeting("Hello World!");if (objCache.size() >= 200000) {objCache.clear();} else {objCache.add(greeting);}return greeting;}
}@Data
@AllArgsConstructor
class Greeting {private String message;
}
就是创建了一个对象池,当对象池中的对象数到达200000时才清空一次,用来模拟年老代对象。
命令启动测试程序:
java -Xmx32m -Xss256k -verbosegc -Xlog:gc*,gc+ref=debug,gc+heap=debug,gc+age=trace:file=gc-%p-%t.log:tags,uptime,time,level:filecount=2,filesize=100m -jar target/demo-0.0.1-SNAPSHOT.jar
我给程序设置的堆的大小为32MB,目的是能让我们看到Full GC。除此之外,我还打开了verbosegc日志,请注意这里我使用的版本是Java 12,默认的垃圾收集器是G1。
使用JMeter压测工具向程序发送测试请求,访问的路径是/greeting。
使用GCViewer工具打开GC日志
上部的蓝线表示已使用堆大小,周期上下震荡,这是对象池要扩展到200000才会清空。
绿线表示新生代GC活动,当堆使用率上去了,会触发频繁GC活动。
竖线表示Full GC,伴随着Full GC,蓝线会下降,这说明Full GC收集了老年代中的对象。
综上,Java堆大小不够:
- GC活动频繁
年轻代GC(绿色线)和年老代GC(黑色线)都比较密集。这说明内存空间不够,也就是Java堆的大小不够。 - Java的堆中对象在GC之后能够被回收
说明不是内存泄漏。
GCViewer还发现累计GC暂停时间有55.57秒:
因此我们的解决方案是调大Java堆的大小,像下面这样:
java -Xmx2048m -Xss256k -verbosegc -Xlog:gc*,gc+ref=debug,gc+heap=debug,gc+age=trace:file=gc-%p-%t.log:tags,uptime,time,level:filecount=2,filesize=100m -jar target/demo-0.0.1-SNAPSHOT.jar
生成的新的GC log分析图如下:
你可以看到,没有发生Full GC,并且年轻代GC也没有那么频繁了,并且累计GC暂停时间只有3.05秒。
总结
CMS来说,我们要合理设置年轻代和年老代的大小。你可能会问该如何确定它们的大小呢?这是一个迭代的过程,可以先采用JVM的默认值,然后通过压测分析GC日志。
如果我们看年轻代的内存使用率处在高位,导致频繁的Minor GC,而频繁GC的效率又不高,说明对象没那么快能被回收,这时年轻代可以适当调大一点。
如果我们看年老代的内存使用率处在高位,导致频繁的Full GC,这样分两种情况:如果每次Full GC后年老代的内存占用率没有下来,可以怀疑是内存泄漏;如果Full GC后年老代的内存占用率下来了,说明不是内存泄漏,我们要考虑调大年老代。
对于G1收集器来说,我们可以适当调大Java堆,因为G1收集器采用了局部区域收集策略,单次垃圾收集的时间可控,可以管理较大的Java堆。
若年轻代和年老代都设置很大,会咋样?
设置过大,回收频率会降低,导致单次回收时间过长,因为需要回收的对象更多,导致GC stop the world时间过长,引起GC停顿时间过长,导致请求无法及时处理
年轻代设置过大
- 生命周期长的对象会长时间停留在年轻代,在S0和S1来回复制,增加复制开销
- 年轻代太大会增加YGC每次停顿的时间,不过通过根节点遍历,OopMap,old scan等优化手段这一部分的开销其实比较少
- 浪费内存
老年代设置过大
- 降低FGC频率,有些堆外内存比如直接内存,需要靠FGC辅佐回收的,就会无法释放。万一剩余的堆外内存不够程序也会宕机
- 单次FGC时间变长,如果在夜深人静的时候主动触发FGC内啥影响,如果白天业务繁忙的时候就凉凉
- 增加YGC时间,old scan阶段会扫描老年代,而且这个阶段耗时在YGC总比重很大
JVM GC原理及调优的基本思路相关推荐
- 全面的GC原理及调优
本文介绍 GC 基础原理和理论,GC 调优方法思路和方法,基于 Hotspot jdk1.8,学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决. 图片来自 Pexels 内容主要如下: G ...
- 第二十一期:老大难的GC原理及调优,这全说清楚了
本文介绍 GC 基础原理和理论,GC 调优方法思路和方法,基于 Hotspot jdk1.8,学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决. 本文介绍 GC 基础原理和理论,GC 调优 ...
- 老大难的GC原理及调优,这下全说清楚了
" 本文介绍 GC 基础原理和理论,GC 调优方法思路和方法,基于 Hotspot jdk1.8,学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决. 文章转载自51CTO技术栈 ...
- JVM GC一次调优实战
参考:http://my.oschina.net/serverx/blog/693628#navbar-header
- JVM原理及调优--网页链接收藏
此篇用于收藏大神们关于JVM原理及调优通俗易懂的文章链接,用于随时查看 JVM调优总结 JVM参数配置大全 JVM调优:选择合适的GC collector 菜菜鸟想了解下大概的JVM内存模型可以看这个 ...
- 性能优化专题 - JVM 性能优化 - 04 - GC算法与调优
目录导航 前言 Garbage Collect(垃圾回收) 如何确定一个对象是垃圾? 引用计数法 可达性分析 垃圾收集算法 标记-清除(Mark-Sweep) 复制(Copying) 标记-整理(Ma ...
- JVM原理和调优的理解和学习
JVM原理和调优的理解和学习 一.详解JVM内存模型 二.JVM中一次完整的GC流程是怎样的 三.GC垃圾回收的算法有哪些 四.简单说说你了解的类加载器 五.双亲委派机制是什么,有什么好处,怎么打破 ...
- java 垃圾回收GC(CMS、G1)原理及调优
概述 本文介绍GC基础原理和理论,GC调优方法思路和方法,基于Hotspot jdk1.8,学习之后将了解如何对生产系统出现的GC问题进行排查解决 阅读时长约30分钟,内容主要如下: GC基础原理,涉 ...
- JVM常用参数以及调优实践
JVM常用参数选项 jvm 可配置的参数选项可以参考 Oracle 官方网站给出的相关信息:http://www.oracle.com/technetwork/java/javase/tech/vmo ...
最新文章
- SPI 的主模式和从模式
- 2018-2019-1 20189204《Linux内核原理与分析》第三周作业
- HashMap?面试?我是谁?我在哪
- 基于区块链的健康链系统设计与实现(4)系统实现
- 前端学习(2143):webpack的config.js配置和package.json
- java 启动redis服务器_docker启动redis并使用java连接
- Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
- dos查看java环境变量_dos命令的识别及环境变量的设置实例
- (原)Lazarus 异构平台下多层架构思路、DataSet转换核心代码
- UVa 1149 Bin Packing 【贪心】
- Mobileye单目测距
- 【区块链】以太坊Solidity编程:智能合约实现之基本语法
- 北京大学肖臻老师《区块链技术与应用》公开课笔记-BTC
- 软件测试自学入门书籍
- day07-python字典
- 微信小程序也许会用到上传视频,针对视频转码转为m3U8格式即web端可以使用的解决办法
- (C语言)signed和unsigned类型转化
- 及时尽孝,别枉读了大学
- 为远程群晖NAS配置固定的公网URL地址 1/2
- 个人电脑虚拟环境的搭建(VMware Workstation Pro)
热门文章
- 实际生产过程中的线性规划问题
- 屏下指纹识别迅速普及,凸显国产手机缺乏核心技术
- 爱笑的人,运气不会太差
- python小海龟画粗细渐变的线_Python 笔记_第一篇_童子功_8. 画图工具 (小海龟 turtle)...
- voliate 关键字
- 蝴蝶落在窗户上的悲哀
- 易优CMS蓝色平面设计广告印刷网站模板源码+带手机版
- 分治算法经典问题---大整数乘法(1~32位大整数乘法)C++
- 【文献阅读】用对比学习做弱监督语义分割(Sung-Hoon Yoon等人,ArXiv,2021)
- c语言结构体内嵌结构体指针_C语言中的结构指针