1.问题描述,部署在客户服务器上的数据网关项目,客户开发反应,出现服务器内存占用很高的问题,怀疑是否为我们部署的项目导致?
开始排查:
一.[endif]登录到客户服务器,首先确认是否是我们应用占用的内存
1. [endif]查看内存占用排行
ps aux | sort -k4,4nr | head -n 10
发现了我们应用进程id的身影
2. [endif]查看数据网关实时内存占用
命令:jps, top - p pid查看实时内存分别看
VmPeak进程所使用的虚拟内存的峰值
VmSize进程当前使用的虚拟内存的大小
VmLck已经锁住的物理内存的大小(锁住的物理内存不能交换到硬盘)
VmHWM进程所使用的物理内存的峰值
VmRSS进程当前使用的物理内存的大小
3. [endif]我不相信,再用cat/proc/pid/status查看网关内存,确认是网关内存泄漏导致。
4. [endif]回去翻一下代码,没看出来泄露的地方
5. [endif]开始尝试调整jvm参数,希望能通过jvm的调优消除(希望肯定是,渺茫的)
使用-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError
调整-Xms,-Xmx,-Xmn等参数,测试环境跑。
参数设置发生oom时生成dump
结果是:oom还是出现了(预期内的)
6. [endif]拿下dump使用jdk中自带的jvisualvm进行分析
Dump导入,看其里面的日志及内存GC等,以及第一次出现oom时的GC报错,反复看,
发现初次报错中,找到一些似曾相识的类关键字面孔,大概猜想是程序的那一段那一句。
7. [endif]本地项目跑起来,使用jvisualvm连接本地jdk监控内存,GC情况,以及对象内存占用情况,同时带着猜想代码段出现问题,可能会出现的情况。看线程的生成。
8. [endif]最后,我发现jvisualvm出现有一个创建线程数达7000加,同一名称。
9. [endif]猜想,因为我存在监控定时任务,对kafka数据补偿补偿。
每隔10秒钟会执行一次检查,检查使用单线程池。
案例代码
KafkaProducer produce =kafkaConf.createProduce();
/**
* 单线程定时随机监听
*/
ExecutorService newSingleThreadExecutor =new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>());
newSingleThreadExecutor.submit(new Runnable() {
@Override
public void run() {
int i =0;
/**
* 调用处理执行重发逻辑
*/省略。。。。。。。
10.出现问题的原因
也就是上面红色的代码,每次都获取了一个新的kafkaProduce对象,在池中有使用,用完后关闭线程池
newSingleThreadExecutor.shutdown();
while (true) {
if (newSingleThreadExecutor.isTerminated()) {
if(logger.isInfoEnabled()){
logger.info("定时重试推送Kafka完成!关闭线程池成功!....");
}
break;
}
}
日志确实是显示关闭了,但是不明白为什么jvisualvm还是里面显示重复创建这个线程。
当然到这里肯定是确认了kafkaProduce对象导致的内存泄露。
11. [endif]解决,将kafkaProduce对象获取使用单例,每次获取到的都是同一个对象就好。
public KafkaProducercreateProduce() {
Properties properties = getkafkaProduceConf();
if (kafkaProducer ==null) {
synchronized (this) {
if (kafkaProducer ==null) {
kafkaProducer =new KafkaProducer<>(properties);
}
}
}
return kafkaProducer;
}
图片不好上传,将就使用文字啦,工作之余,记录自己平时的一些小毛病,以及问题排查的思路。记录自己的经验和不足之处,笔记中可能会有很多不足之处,欢迎各位留言指正讨论。