【jvm】来自于线上的fullGC分析
系统最近老年代的内存上升的比较快,三到四天会发生一波fullGC。于是开始对GC的情况做一波分析。
线上老年代2.7G,年轻带1.3G老年代上升较快,3天一波fullGC,并且fullGC会把内存回收,有时回收一般,有时回收全部。所以判断是不会有内存泄漏现象的,内存发生泄漏是回收不了的。第二个判断,不存在大对象,一个是基于对程序的理解,一个是对于老年代上升的速率,基本是稳固上升。不存在峰值。
我是先用jstack 命令打印出线程状态的 jstack -l pid >> 文件名 发现这么操作是无用的。jstack主要是分析线程状态的,尤其是针对占用CPU,死循环,死锁操作等。
一开始确实定位到一个线程,是我们服务每个一分钟拉去配置中心数据的线程,数据量很小,而且基本都是重复的数据。所以那会没有头绪方向不对。
后来老大说打下内存快照啊。于是就开始看下内存快照了。
1、先使用 jmap命令打印出内存快照文件。
jmap -dump:format=b,file= 文件路径 pid(进程号)
2、使用MemoryAnalyzer(windows下的分析工具)工具分析大对象
分析出来是jdbc连接对象实例太多,因为mysql的jdbc包在每次创建jdbc连接的时候,会对连接进行虚引用包装,最后放到一个ConcurrentHashMap里。而系统1分钟会进行年轻代的GC,而程序配置的链接生命周期为30分钟。所以30分钟MinGC将到达30多次,而JVM默认的配置是15。所以我们推测这个配置的不对,要么减少生命周期要么增加JVM年龄的配置。
经过测试在jdk8的环境下,年龄的阈值是不能超过15的,这就尴尬了。于是只好降低生命周期,同时设置数据源的最大连接和最小连接相等,防止数据源一直创建空闲连接,这样能方式连接数量,同时通过反射,检测ConcurrentHashMap的数量。经过上线后,发现并没有明显效果。于是乎干脆把map的数据直接清空,发现清空了上完线也并没有解决老年代上升的问题。不过老年代每次fullGC都会回收到底。于是判断清空有助于进行回收,并且老年代回收的耗时也变短了,减少到一半时间。
另一方面观测,检查了JVM年轻代 E区和S区的比例,你会认为默认是1:8,但是不是那么简单地。发现程序刚启动时确实是1比8,但是随着程序启动,MinGC,jvm默认的垃圾回收器会自动调节这个比例。只有设置CMS才不会自动调节。于是决策是由于S区分配内存太小导致,当时我们的S区稳定在30M左右,E区差不多有1.3G。于是改成了CMS垃圾回收器,发现也并没有卵用,而且老年代内存到达50%就发生了fullGC,看来CMS是可以设置回收阈值的。实在没办法了,只好换数据源了,把HikairCP换成了druid。发现换了之后就解决了,fullGC频率从4天能到达15天,ConcurrentMap的数量很少,最多也就2百的量。而之前能达到上万的量。总之不知道是因为HikairCP的原因,还是我们配置的原因。据说这个数据源号称比druid速度更优的数据源。
.
.
.
历史是何其的相似,程序性能问题又来了,我们程序为了提高tps,经过压测,发现瓶颈在于redis,当时redis是哨兵模式,读写访问一个master,于是想切换为集群。发现切换为集群后,fullGC的频率又回到了原先的地步,而且把操作切换为读集群时,压力更大,竟然再短短5小时内,打满老年代内存, 并且CPU占用也较大。没办法观测了一会,发现每5分钟上升2个百分点。代码回滚了。于是乎又开始了一波分析,代码优化。
经过分析本次上线只是添加了cluster管道批量操作的代码,代码肯定出现在这里,于是开始进行优化。
首先分析线上内存快照,发现最大占用的是 int 数组,这个不好分析,应为属于基本对象,没法分析。于是乎私底下在本地进行读集群方法的压测,再加上代码的分析,发现每次操作都会对取cluster集群的信息,这个集群的信息包含了所有节点的ip+port,以及他们对应的分槽信息,而这个分槽信息是以LinkedHashSet类型存储的,里面是满满的Integer类型的槽。一共16384个,于是基本确定了,是每次实时查询cluster集群信息导致的。发现Jedis的方法是对集群信息做了缓存的 ,但是只缓存了100毫秒。再加上我们本身程序也包含了大量的业务逻辑。估计这么一连起来就造成了YGC次数过高,或者年轻代S区空间太小。于是从两个方面去提高程序性能。
发现在修改JVM参数配比,提高年轻代S区的内存大小,并且修改垃圾回收器为CMS,发现在本机进行ab压测,最高tps到达了800。并且在并发80,访问量50000的情况下,老年代内存会上升到100M,YGC次数能够到达200。而哨兵的压测结果tps最高到达2000。而且老年代内存50M。YGC次数也在30以内。
于是对cluster的代码进行优化,把每次获取集群节点信息放到了for循环里。tps提高到1300,老年代内存稳定在90M。
我们程序另一条思路是:把集群的信息缓存到JVM,不依靠Jedis的缓存100ms。而是由我们自己缓存。但是存在一个问题,每当集群节点扩容,分槽或者主下线,从上线。我们的程序就检测不到变化了,于是在程序里开启了异步线程每个5秒刷新集群信息,而且对于每次操作cluster做了重试处理,一旦检测到节点连接创建失败,发生MOVED错误,就刷新集群信息,进行操作重试。发现使用静态缓存集群信息的方式,tps提高到2000左右,基本和哨兵持平,而且老年代内存也到达60M左右,YGC次数也降下来了。
在这些过程中,我学到了什么:
自己尝试搭建了虚拟机的redis-cluster集群(很早就搭建了,这回正好用上),redis集群分槽。
cluster管道批量操作,jdk的一些常用工具命令,还有ab的简单压测
最后重要的是分析问题的能力和思路
【jvm】来自于线上的fullGC分析的更多相关文章
- 关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程
某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC.解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导. 1. 生成dump文件 为了定位FullGC ...
- 记一次线上OOM问题分析与解决
一.问题情况 最近用户反映系统响应越来越慢,而且不是偶发性的慢.根据后台日志,可以看到系统已经有oom现象. 根据jdk自带的jconsole工具,可以监视到系统处于堵塞时期.cup占满,活动线程数持 ...
- 我整理的一份来自于线上的Nginx配置(Nginx.conf),希望对学习Nginx的有帮助
我整理了一份Nginx的配置文件说明,是真正经历过正式线上考验过.如果有优化的地方,也请朋友们指点一二,整理出一份比较全而实用的配置. 主要包含配置:负载均衡配置,页面重定向,转发,HTTPS和HTT ...
- 一次线上mysql死锁分析
一.现象 发运车次调用发车接口时发生异常,后台抛出数据库死锁日志. 二.原因分析 通过日志可以看出事务T1等待 heap no 8的行锁 (X locks 排他锁) 事务T2持有heap no 8的行 ...
- 记一次线上频繁fullGc的排查解决过程
发生背景 最近上线的一个项目几乎全是查询业务,并且都是大表的慢查询,sql优化是做了一轮又一轮,前几天用户反馈页面加载过慢还时不时的会timeout,但是我们把对应的sql都优化一遍过后,前台响应还是 ...
- 快速定位java系统的线上问题--转
原文地址:http://m.blog.csdn.net/article/details?id=43376943 前言:我们的场景并没有像BAT等大型互联网公司里的系统那么复杂,但是基本上也有一定的规模 ...
- 线上故障排查——drools规则引擎使用不当导致oom
事件回溯 1.7月26日上午11:34,告警邮件提示:tomcat内存使用率连续多次超过90%: 2.开发人员介入排查问题,11:40定位到存在oom问题,申请运维拉取线上tomcat 内存快照dum ...
- JVM jmap dump 分析dump文件 / 如何使用Eclipse MemoryAnalyzer MAT 排查线上问题
jhat简介 jhat用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 这个工具并不是想用于应用系统中而是用于"离线" ...
- 线上系统/tmp 目录不断增长分析与总结
1.问题描述 系统配置为单核4G, web 工程配置堆2G, /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧. 2.分析 /tmp 目录存放系统运行 ...
随机推荐
- php rsa理解
参考链接:http://www.cnblogs.com/firstForEver/p/5803940.html 自己封装的一个类: <?php class CRsaAuthorization { ...
- Docker安装Zookeeper
⒈下载 docker pull zookeeper ⒉运行 docker run --name zk -p 2181:2181 -p 2888:2888 -p 3888:3888 --restart ...
- python 历险记(四)— python 中常用的 json 操作
目录 引言 基础知识 什么是 JSON? JSON 的语法 JSON 对象有哪些特点? JSON 数组有哪些特点? 什么是编码和解码? 常用的 json 操作有哪些? json 操作需要什么库? 如何 ...
- VxWorks软件开发项目实例完全解析1-VxWorks简介
1.前言 VxWorks是专门为实时嵌入式系统设计开发的32位操作系统.主要有如下特点: 实时性强 支持多任务 体积小可裁剪 支持多种CPU 支持网络通信串口通信 汇编+标准C的编程模式.支持C++ ...
- mysql系列十三、mysql中replace into和duplicate key的使用区
一.创建测试表 1.创建唯一索引"b" CREATE TABLE `test2` ( `id` int(10) NOT NULL AUTO_INCREMENT, `a` varch ...
- C# 汉字与区位码之间的相互转换(中文数字字母可以,支持空格,但是特殊字符未来得及测试)
using System; using System.Text; namespace Test { class MainClass { /// <summary> /// 中文空白字符,用 ...
- 012_如何清除DNS缓存
运维过程中经常会进行切换域名解析等的操作,就需要查看是否更新.但常常DNS设置已经更新了,但是用户那边的DNS还是没有更新. 以下分析几点原因及我的解决方案. 一. <1>本地你的dns缓 ...
- OneNET麒麟座应用开发之八:采集大气压力等环境参数
采集大气压力和温度也是核算大气标准状况下的各种数据的必须参数,为此我们必须知道压力和温度才能计算标准状况下的各种参数,于此我们需要一个既能检测压力也能检测温度的元件. 1.硬件概述 MS5837压力传 ...
- STM32应用实例七:与宇电设备实现AI-BUS通讯
宇电的设备使用基于RS-485的自定义协议,协议本身比较简单,只有2条指令: 读:地址代号+52H(82) +要读的参数代号+0+0+校验码 写:地址代号+43H(67)+要写的参数代号+写入数低字节 ...
- python之__dict__与dir(转载)
Python下一切皆对象,每个对象都有多个属性(attribute),Python对属性有一套统一的管理方案. __dict__与dir()的区别: dir()是一个函数,返回的是list: __di ...