问题描述

  • 应用收到频繁Full GC告警

问题排查

  • 登录到对应机器上去,查看GC日志,发现YGC一分钟已经达到了15次,比Full GC还要频繁一些,其中Full GC平均10分钟超过了4次,如下图

  • 使用jstat -gcutil 5280 1000查看实时GC情况,年老代采用的是CMS收集器,发现触发Full GC的原因是年老代占用空间达到指定阈值70%(-XX:CMSInitiatingOccupancyFraction=70)。
  • 这时候猜测是某个地方频繁创建对象导致,通过jmap -dump:format=b,file=temp.dump 5280 dump文件,然后下载到本地通过jvisualvm分析对象的引用链的方式来定位具体频繁创建对象的地方,dump文件下载下来有5G多,整个导入过程都花了10多分钟。想查看所占空间较多对象的引用链,直接OOM了,dump对象太大了。这时候就换了种思路,查看占用空间比较大的一系列对象,看能不能找出什么端倪。占用空间最大的几类对象如下图



    发现排第一的chart[]对象里面,存在一些metrics监控的具体指标的相关内容,排第二的io.prometheus.client.Collector$MetricFamilySample$Sample和排第9和第13对象都是spring boot中metrics指标监控相关的对象,所以此时怀疑metrics监控的某个地方在频繁创建对象,首先考虑的是否因为metrics指标太多导致的,于是登录线上机器curl localhost:8080/mertrics > metrics.log,发现响应内容有50多M,参考其他相关的正常应用,指标总共内容也就10多M左右,打开指标内容发现了很多类似如下图的指标




    看到了这里已经可以确定代码中上报这个指标是存在问题的,并没有达到我们想要的效果,所以也怀疑也是这个地方导致的Full GC频繁。

问题初步解决

  • 由于这个指标也无关紧要,初步解决方案就把上报该指标的代码给干掉。上线后看下Full GC问题是否会得到改善,果然,上线后Full GC告警问题已经解决。

初步解决后的思考,为什么会有这个问题?

  • 外部监控系统,每25s会来调用metrics这个接口,这个接口会把所有的metrics指标转成字符串然后作为http响应内容响应。监控每来调用一次就会产生一个50多M的字符串,导致了频繁YGC,进而导致了晋升至年老代的对象也多了起来,最终年老代内存占用达到70%触发了Full GC。

根源问题重现

  • 此处采用metrics的作用:统计线程池执行各类任务的数量。为了简化代码,用一个map来统计,重现代码如下
    import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; /**
* 线程池通过submit方式提交任务,会把Runnable封装成FutureTask。
* 直接导致了Runnable重写的toString方法在afterExecute统计的时候没有起到我们想要的作用,
* 最终导致几乎每一个任务(除非hashCode相同)就按照一类任务进行统计。所以这个metricsMap会越来越大,调用metrics接口的时候,会把该map转成一个字符返回
*/
public class GCTest {
/**
* 统计各类任务已经执行的数量
* 此处为了简化代码,只用map来代替metrics统计
*/
private static Map<String, AtomicInteger> metricsMap = new ConcurrentHashMap<>(); public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()){
/**
* 统计各类任务执行的数量
* @param r
* @param t
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
metricsMap.compute(r.toString(), (s, atomicInteger) ->
new AtomicInteger(atomicInteger == null ? 0 : atomicInteger.incrementAndGet()));
}
};
/**
* 源源不断的任务添加进线程池被执行
*/
for (int i =0; i < 1000; i++) {
threadPoolExecutor.submit(new SimpleRunnable());
}
Thread.sleep(1000 * 2);
System.out.println(metricsMap);
threadPoolExecutor.shutdownNow();
}
static class SimpleRunnable implements Runnable{ @Override
public void run() {
System.out.println("SimpleRunnable execute success");
}
/**
* 重写toString用于统计任务数
* @return
*/
@Override
public String toString(){
return this.getClass().getSimpleName();
}
}
}

最终解决

  • 可以把submit改成execute即可

总结

  • 以上重显代码可以看出metricsMap中的元素是会越来越多的。如果就这样下去,最终的结果也会出现OOM。
  • 根本原因还是对ThreadPoolExecutor不够熟悉,所以出现了这次问题。
  • 个人感觉Full GC类问题是比较让人头疼的。这些问题并不会想代码语法问题一样,ide会提示我们具体错在哪里,我们只要修改对应地方基本都能解决。造成Full GC频繁的原因也有很多,比如可能是jvm参数设置不合理、Metaspace空间触发、频繁创建对象触发等等。
  • 如果确定了是频繁创建对象导致,那么接下来的目的就是确定频繁创建对象的对应代码处,这时候可以选择通过dump线上堆栈,然后下载到本地。选择一些可视化分析工具进行分析。最终定位到出问题的代码处,然后解决问题。

版权声明

作者:wycm

出处:https://www.cnblogs.com/w-y-c-m/p/9919717.html

您的支持是对博主最大的鼓励,感谢您的认真阅读。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

一次频繁Full GC问题排查过程分享的更多相关文章

  1. 一次CMS GC问题排查过程(理解原理+读懂GC日志)

    这个是之前处理过的一个线上问题,处理过程断断续续,经历了两周多的时间,中间各种尝试,总结如下.这篇文章分三部分: 1.问题的场景和处理过程:2.GC的一些理论东西:3.看懂GC的日志 先说一下问题吧 ...

  2. [转]一次CMS GC问题排查过程(理解原理+读懂GC日志)

    这个是之前处理过的一个线上问题,处理过程断断续续,经历了两周多的时间,中间各种尝试,总结如下.这篇文章分三部分: 1.问题的场景和处理过程:2.GC的一些理论东西:3.看懂GC的日志 先说一下问题吧 ...

  3. 记录一次数据库CPU被打满的排查过程

    1 前言 近期随着数据量的增长,数据库CPU使用率100%报警频繁起来.第一个想到的就是慢Sql,我们对未合理运用索引的表加入索引后,问题依然没有得到解决,深入排查时,发现在 order by id ...

  4. 一次kibana服务失败的排查过程

    公司在kubernetes集群上稳定运行数月的kibana服务于昨天下午突然无法正常提供服务,访问kibana地址后提示如下信息: 排查过程: 看到提示后,第一反应肯定是检查elasticsearch ...

  5. 解Bug之路-记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,正好博客也好久不更新了,就以此为素材写出了本篇文章 ...

  6. 解Bug之路-记一次存储故障的排查过程

    解Bug之路-记一次存储故障的排查过程 高可用真是一丝细节都不得马虎.平时跑的好好的系统,在相应硬件出现故障时就会引发出潜在的Bug.偏偏这些故障在应用层的表现稀奇古怪,很难让人联想到是硬件出了问题, ...

  7. 记一次生产环境Nginx日志骤增的问题排查过程

    摘要:众所周知,Nginx是目前最流行的Web Server之一,也广泛应用于负载均衡.反向代理等服务,但使用过程中可能因为对Nginx工作原理.变量含义理解错误,或是参数配置不当导致Nginx工作异 ...

  8. 干货!一次kafka卡顿事故排查过程

    由于一次功能上线后,导致某数据量急剧下滑,给我们紧张的呢!排查过程也是个学习过程(这其中有大部分是领导们的功劳,不过分享给大家应该也不犯法吧,ᐓ) 1. 确认问题的真实性? 被数据部门告知,某数据量下 ...

  9. Linux(2)---记录一次线上服务 CPU 100%的排查过程

    Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...

随机推荐

  1. 进程保护--CrossThreadFlags标志位

    原理: 1. 将进程的所有线程的线程CrossThreadFlags标志位设置成Terminated或者System. 效果:任务管理器,WSYSCheck,ICESWORD无法结束进程.. 但PCH ...

  2. Java三方---->pdf框架之IText的使用

    在企业的信息系统中,报表处理一直占比较重要的作用t.通过在服务器端使用Jsp或JavaBean生成PDF报表,客户端采用超链接显示或下载得到生成的报表,这样就很好的解决了B/S系统的报表处理问题.今天 ...

  3. JavaBean与Map<String,Object>相互转换

    一.为什么要实现javaBean与Map<String,Object>相互转换 Spring中的BaseCommandController对象可以将传递过来的参数封装到一个JavaBean ...

  4. [Java语言] HashMap,HashSet,Hashtable,Vector,ArrayList 的关系 <转>

    这么几个比较常用的但是比较容易混淆的概念同出于 java.util 包.本文仅作几个类的浅度解析. (本文基于JDK1.7,源码来自openjdk1.7.) ├── Collection │ ├── ...

  5. Python的函数名作为参数传入调用以及map、reduce、filter

    零.python的lambda函数: #lambda function func = lambda x : x+1 #这里是一个匿名函数,x是参数,x+1是对参数的操作 func(1)= 2 多个参数 ...

  6. CentOS下安装cvechecker并进行主机基线安全检查

    一.cvechecker的安装 1.首先下载cvechecker并解压该文件: cd /home/username mkdir cve wget https://raw.githubuserconte ...

  7. 解决在微信中部分IOS不能自动播放背景音乐

    前言在做各种HTML5场景页面的时候,插入背景音乐是一个很普遍的需求.我们都知道,IOS下的safari是无法自动播放音乐的,以至一直以来造成一种错误的认识,iso是无法自动播放媒体资源的.直到微信火 ...

  8. Git的配置和使用

    eclipse中Git的配置 可以参考http://www.cnblogs.com/zhxiaomiao/archive/2013/05/16/3081148.html, http://blog.cs ...

  9. Thinkphp的cookie的怎么玩?

    在使用COOKIE的时候,首先要对COOKIE进行加密,加密方式采用:异位或的方式进行加密: // 异位或加密 1是加密 0 是解密 function encrytion($value,$type=0 ...

  10. Ubbeditor的使用

    简单介绍: 作为一种放在客户端文本编辑器,此时不能支持将JS.Html代码直接发送给服务器,这样将会给服务器带来极大的危险,比如UMEditor(富文本编辑器),它的使用需要关闭服务器端的代码检查的, ...