系统最近老年代的内存上升的比较快,三到四天会发生一波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分析的更多相关文章

  1. 关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程

    某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC.解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导. 1. 生成dump文件 为了定位FullGC ...

  2. 记一次线上OOM问题分析与解决

    一.问题情况 最近用户反映系统响应越来越慢,而且不是偶发性的慢.根据后台日志,可以看到系统已经有oom现象. 根据jdk自带的jconsole工具,可以监视到系统处于堵塞时期.cup占满,活动线程数持 ...

  3. 我整理的一份来自于线上的Nginx配置(Nginx.conf),希望对学习Nginx的有帮助

    我整理了一份Nginx的配置文件说明,是真正经历过正式线上考验过.如果有优化的地方,也请朋友们指点一二,整理出一份比较全而实用的配置. 主要包含配置:负载均衡配置,页面重定向,转发,HTTPS和HTT ...

  4. 一次线上mysql死锁分析

    一.现象 发运车次调用发车接口时发生异常,后台抛出数据库死锁日志. 二.原因分析 通过日志可以看出事务T1等待 heap no 8的行锁 (X locks 排他锁) 事务T2持有heap no 8的行 ...

  5. 记一次线上频繁fullGc的排查解决过程

    发生背景 最近上线的一个项目几乎全是查询业务,并且都是大表的慢查询,sql优化是做了一轮又一轮,前几天用户反馈页面加载过慢还时不时的会timeout,但是我们把对应的sql都优化一遍过后,前台响应还是 ...

  6. 快速定位java系统的线上问题--转

    原文地址:http://m.blog.csdn.net/article/details?id=43376943 前言:我们的场景并没有像BAT等大型互联网公司里的系统那么复杂,但是基本上也有一定的规模 ...

  7. 线上故障排查——drools规则引擎使用不当导致oom

    事件回溯 1.7月26日上午11:34,告警邮件提示:tomcat内存使用率连续多次超过90%: 2.开发人员介入排查问题,11:40定位到存在oom问题,申请运维拉取线上tomcat 内存快照dum ...

  8. JVM jmap dump 分析dump文件 / 如何使用Eclipse MemoryAnalyzer MAT 排查线上问题

    jhat简介 jhat用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 这个工具并不是想用于应用系统中而是用于"离线" ...

  9. 线上系统/tmp 目录不断增长分析与总结

    1.问题描述 系统配置为单核4G, web 工程配置堆2G,  /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧. 2.分析 /tmp 目录存放系统运行 ...

随机推荐

  1. php 全局变量问题

    当在函数里通过require_once包含另外php文件. 而另外php文件包含了另外php文件,而该php文件的函数需要另外的php文件. 例子: installment_maintenance_s ...

  2. [转]python 装饰器

    以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读 ...

  3. kali linux 下搭建git服务器

    参考:http://www.cnblogs.com/dee0912/p/5815267.html https://www.liaoxuefeng.com/wiki/001373951630592960 ...

  4. oracle删除表字段和oracle表增加字段

    这篇文章主要介绍了oracle表增加字段.删除表字段修改表字段的使用方法,大家参考使用吧   添加字段的语法:alter table tablename add (column datatype [d ...

  5. 『转载』Matlab中fmincon函数获取乘子

    Matlab中fmincon函数获取乘子 一.输出结构 [x,fval,exitflag,output,lambda] = fmincon(......) 二.结构说明 lambda结构 说     ...

  6. 【转】如何安装JDK以及配置Java运行环境

    具体的参考这篇博文就好了~~!http://www.cnblogs.com/liu-en-ci/p/6743106.html

  7. GetStockObject 理解

    原文地址:https://www.cnblogs.com/Clingingboy/archive/2013/04/13/3017952.html GetStockObject在图形编程中是常用API之 ...

  8. Project Euler Problem6

    Sum square difference Problem 6 The sum of the squares of the first ten natural numbers is, 12 + 22  ...

  9. 转载:《理解OAuth 2.0》 阮一峰

    原文:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛 ...

  10. html中子界面与父界面相互操作或传值

    一.在使用iframe的页面,要操作这个iframe里面的DOM元素可以用: contentWindow.contentDocument(测试的时候chrom浏览器,要在服务器环境下) content ...