博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM深度好文,CMS 收集器与 GC 日志分析定位问题详解
阅读量:2199 次
发布时间:2019-05-03

本文共 5132 字,大约阅读时间需要 17 分钟。

在这里插入图片描述

CMS 收集器实战

最新2020整理收集的一线互联网公司面试真题(都整理成文档),有很多干货,包含netty,spring,线程,spring cloud等详细讲解,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需: 暗号:CSDN在这里插入图片描述

实战开始,准备好了没

在这里插入图片描述

在这里插入图片描述

模拟业务场景代码:

@RestControllerpublic class IndexController {
/*** * 存 big 对象 * @return */@GetMapping("/put")public String process() {
ArrayList
users = queryUsers(); for (User user:users){
//TODO 业务操作 } return "ok";}private ArrayList
queryUsers() {
ArrayList
users = new ArrayList<>(); for (int i = 0; i < 50000; i++) {
users.add(new User(i, "java2b")); } return users;}}
public class User {
private int id; private String name; private byte[] data; public User(int id, String name) {
this.id = id; this.name = name; data=new byte[1 * 128 * 1024]; }}

输出收集器信息:

/*** * 打印 jvm 信息 * @return */@GetMapping("/info")public String info() {
List
garbages = ManagementFactory.getGarbageCollectorMXBeans(); StringBuilder stringBuilder = new StringBuilder(); for (GarbageCollectorMXBean garbage : garbages) {
stringBuilder.append("垃圾收集器:名称=" + garbage.getName() + ",收集=" + garbage.getCollectionCount() + ",总花费时间=" + garbage.getCollectionTime()); // + ",内存区名称=" + Arrays.deepToString(garbage.getMemoryPoolNames())); stringBuilder.append("\r\n"); } MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); MemoryUsage headMemory = memory.getHeapMemoryUsage(); long MB = 1024 * 1024; stringBuilder.append("head 堆:"); stringBuilder.append("\t 初始(M):" + headMemory.getInit() / MB); stringBuilder.append("\t 最大(上限)(M):" + headMemory.getMax() / MB); stringBuilder.append("\t 当前(已使用)(M):" + headMemory.getUsed() / MB); stringBuilder.append("\t 提交的内存(已申请)(M):" + headMemory.getCommitted() / MB); stringBuilder.append("\t 使用率:" + headMemory.getUsed() * 100 / headMemory.getCommitted() + "%"); return stringBuilder.toString();}

生成 jar 包部署到服务器

启动参数:

java -Xms256m -Xmx256m -verbose:gc -Xloggc:/root/jvm/gc-cms.log -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintHeapAtGC -XX:HeapDumpPath=/root/jvm/dump.hprof -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCTimeStamps -XX:+PrintCommandLineFlags -XX:+PrintFlagsFinal -XX:+PrintGCDetails -XX:+UseCMSCompactAtFullCollection -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=6666 -Djava.rmi.server.hostname=192.168.0.31 -jar /root/jvm/jvm-web-0.0.1-SNAPSHOT.jar > catalina.out &

这儿要插播下 JVM 参数意义.

JVM 参数详解:

JVM 参数 含义

-XX:-CMSPrecleaningEnabled

不进行预清理,度过我们之前的文章的都知道,CMS 在并发标记和重新标记的这段时间内,会有一个预清理的工作,而这个通过会尝试 5 秒之内等待来一次 YGC。以免在后面的重新标记阶段耗费大量时间来标记新生代的对象。

-XX:+UseConcMarkSweepGC

此参数将启动 CMS 回收器。默认新生代是 ParNew,也可以设置 Serial 为新生代收集器。该参数等价于 -Xconcgc。

-XX:ParallelGCThreads

由于是并行处理器,当然也可以指定线程数。默认并发线程数是:(ParallelGCThreads + 3)/ 4)。
-XX:ConcGCThreads
或者 -XX:ParallelCMSThreads ;除了上面设置线程的方式,你也可以通过这个两个参数任意一个手工设定 CMS 并发线程数

-XX:CMSInitiatingOccupancyFraction

由于 CMS 回收器不是独占式的,在垃圾回收的时候应用程序仍在工作,所以需要留出足够的内存给应用程序,否则会触发 FGC。而什么时候运行 CMS GC 呢?通过该参数即可设置,该参数表示的是老年代的内存使用百分比。当达到这个阈值就会执行 CMS。默认是 68。 如果老年代内存增长很快,建议降低阈值,避免 FGC,如果增长慢,则可以加大阈值,减少 CMS GC 次数。提高吞吐量。

-XX:+UseCMSCompactAtFullCollection

由于 CMS 使用标记清理算法,内存碎片无法避免。该参数指定每次 CMS 后进行一次碎片整理。

-XX:CMSFullGCsBeforeCompaction 由于每次进行碎片整理将会影响性能,你可以使用该参数设定多少次 CMS 后才进行一次碎片整理,也就是内存压缩。

-XX:+CMSClassUnloadingEnabled

允许对类元数据进行回收。

-XX:CMSInitiatingPermOccupancyFraction

当永久区占用率达到这一百分比时,启动 CMS 回收(前提是 -XX:+CMSClassUnloadingEnabled 激活了)。

-XX:UseCMSInitiatingOccupancyOnly

表示只在到达阈值的时候才进行 CMS 回收。

XX:CMSWaitDuration=2000

由于 CMS GC 条件比较简单,JVM 有一个线程定时扫描 Old 区,时间间隔可以通过该参数指定(毫秒单位),默认是 2s。
JVM 工具参数:
JVM 参数 含义


-XX:+PrintGCDateStamps 打印 GC 日志时间戳

-XX:+PrintGCDetails 打印 GC 详情
-XX:+PrintGCTimeStamps 印此次垃圾回收距离 jvm 开始运行的所耗时间
-Xloggc: 将垃圾回收信息输出到指定文件
-verbose:gc 打印 GC 日志
-XX:+PrintGCApplicationStopedTime 查看 gc 造成的应用暂停时间
XX:+PrintTenuringDistribution 对象晋升的日志
-XX:+HeapDumpOnOutOfMemoryError 内存溢出时输出 dump 文件

在这里插入图片描述

启动效果:在这里插入图片描述

访问:在这里插入图片描述

请求 put: 我们通过 http 访问 put 方法之后看看效果:在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在运行的过程中我们发现有大量的对象进入老年代,触发了 full gc,cms 一直在收集。

使用率达到 99%,cms 也一刻没停下:在这里插入图片描述

日志分析在这里插入图片描述

日志分析 1.0 版本: 我们抽取一条日志来分析下

[GC (Allocation Failure) 0K->63K(64K), 0.0047147 secs] 10258K->6780K(46144K), [Metaspace: 3434K->3434K(1056768K)], 0.0047613 secs][Times: user=0.02 sys=0.00, real=0.00 secs] 该日志为四个部分:

Full GC:

表明进行了一次垃圾回收,前面没有 Full 修饰,表明这是一次 Minor GC ,注意它不表示只 GC 新生代,并且现有的不管是新生代还是老年代都会 STW。

Allocation Failure:

表明本次引起 GC 的原因是因为在年轻代中没有足够的空间能够存储新的数据了。

10258K->6780K(46144K),:单位是 KB

三个参数分别为:GC 前该内存区域(这里是年轻代)使用容量,GC 后该内存区域使用容量,该内存区域总容量。

0.0047613 secs:

该内存区域 GC 耗时,单位是秒

[Times: user=0.04 sys=0.00, real=0.01 secs]:

分别表示用户态耗时,内核态耗时和总耗时 在这里插入图片描述

日志分析 2.0 版本:

采用在线 gceasy 来进行分析,我们打开网址,然后上传我们生产的 gc 日志,如图所示: 在这里插入图片描述

优化问题:列出了可以优化的 4 个问题新生代和老年代元空间内存占用情况 在这里插入图片描述

吞吐量统计:97.39%

在这里插入图片描述
各各分代的内存变化
在这里插入图片描述

CMS 垃圾收集器不同时期发生的耗时

在这里插入图片描述

在这里插入图片描述
GC 发生次数的分类和耗时情况
在这里插入图片描述

定位问题

我们通过生产的快照文件来定位问题: 在这里插入图片描述

JProfiler:

下载到本地通过 JProfiler 打开查看 在这里插入图片描述

查看大对象在这里插入图片描述
我们不难发现是 ArrayList 集合占用了 96%的内存,那我们来看看哪块代码大量用到了我们 ArrayList 集合了在这里插入图片描述
找到对应代码在这里插入图片描述
通过此代码我们就发现 put 方法大量用到了 ArrayList 集合造成的内存溢出 OOM

总结

上述实战相信大家都明白了,大致流程就是:

1、够将 SpringBoot 项目 模拟真实大批量用户场景

2、配置 JVM 参数然后部署运行监控数据生成日志文件

3、通过分析日志文件确认问题。

需要上文代码和软件的朋友,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需: 暗号:CSDN 大家可以自己实操下加深印象。在这里插入图片描述

转载地址:http://sehub.baihongyu.com/

你可能感兴趣的文章
手把手用 IntelliJ IDEA 和 SBT 创建 scala 项目
查看>>
GAN 的 keras 实现
查看>>
AI 在 marketing 上的应用
查看>>
Logistic regression 为什么用 sigmoid ?
查看>>
Logistic Regression 为什么用极大似然函数
查看>>
SVM 的核函数选择和调参
查看>>
LightGBM 如何调参
查看>>
用 TensorFlow.js 在浏览器中训练神经网络
查看>>
cs230 深度学习 Lecture 2 编程作业: Logistic Regression with a Neural Network mindset
查看>>
梯度消失问题与如何选择激活函数
查看>>
为什么需要 Mini-batch 梯度下降,及 TensorFlow 应用举例
查看>>
为什么在优化算法中使用指数加权平均
查看>>
什么是 Q-learning
查看>>
用一个小游戏入门深度强化学习
查看>>
5 分钟入门 Google 最强NLP模型:BERT
查看>>
初探Java设计模式4:一文带你掌握JDK中的设计模式
查看>>
初探Java设计模式5:一文了解Spring涉及到的9种设计模式
查看>>
Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
查看>>
Java集合详解2:一文读懂Queue和LinkedList
查看>>
Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
查看>>