使用 JDK 命令行工具分析内存泄漏或内存溢出问题

最近遇到一个棘手的问题,有业务方在调用存储系统封装的 SDK 取数的过程中,遇到了 OOM 问题,但是数据量很小,只有 12000 条。同时进程启动时申请的内存高达 12g,使用 Xmx、Xms 参数控制,实际指定参数取值为:-Xms12g -Xmx12g。但是如果只看报错日志信息,抛出异常的代码位置指向了 SDK 的内部代码。根据这个现象,我猜测可能是业务方的处理逻辑问题、SDK 内部处理逻辑问题、申请的内存过小问题,这些问题归根结底,要么是内存不够【内存溢出】,要么是内存不当使用【内存泄漏】。所以,我要在 Java 虚拟机参数方面或者业务方代码逻辑方面入手,一步一步测试,找出问题的元凶。本文就记录这一过程,以及适当引申一些关于 JVM 的知识。

解释说明一下,上述中的 SDK 表示存储系统独立封装的取数、查询接口,它屏蔽了 Elasticsearch 自带的接口,并封装成公共组件,提供给各个业务方使用。各个业务方在使用前,需要申请开通 token 验证码,存储系统会根据业务方的使用量分配合适的资源,业务方在调用时需要传入 token 验证。这样做的好处,一是可以监控所有的业务方的取数、查询情况,收集所有的请求日志,统计一些常用的指标,然后反过来指导存储系统的改进,例如根据业务方的调用情况进行资源分配的伸缩、针对常用的数据类型进行索引优化。二是可以保障整个数据库集群的正常运行,由于屏蔽了 Elasticsearch 自带的接口,业务方不能随意操作超大额的数据量,SDK 会做限制,因此不会产生某些不合理的查询、取数请求,从而不对数据库造成巨大的压力。三是限制了一些不需要的查询、取数方式,在保障业务方基本需求的情况下又可以保障数据库集群的稳定,例如多层聚合、日期聚合等操作,这些操作不合理,而且会对数据库集群造成压力【无论数据量大小都可能会出事】。

问题出现

简单描述现象,查看日志,猜测可能的原因。

问题解决

分析现象

一开始没有指定 JVM 参数,因为使用的是 JDK1.7 版本的参数,不会生效,这就导致分配的默认堆取值偏小。

JVM 参数设置:

1
2
3
4
5
6
7
8
JAVA=${JAVA_HOME}/bin/java
# 设置 jvm 的参数
#HEAP_OPTS="-Xms12g -Xmx12g"
HEAP_OPTS="-Xms6g -Xmx6g -Xmn2g"
# JDK8 以后取消了 PermSize
#PERM_OPTS="-XX:PermSize=1024M -XX:MaxPermSize=2048m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC"
#JDK8 的 MetaspaceSize
PERM_OPTS="-XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=2048m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError"

待整理,重试,重现现象,确保问题准确复现。

掌握内存分析工具

待整理。各种工具介绍,举例,截图。

对症下药解决问题

待整理。减小内存,使用普通的 list。
重试,使用命令行工具查看现象,截图。

问题总结

不同版本的参数不一致

主要是针对 JDK 来说的,不同的 JDK 版本的参数会有一些不同,例如以下 2 个虚拟机参数,在 JDK1.8 的环境中是 -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=2048m,已经不是 -XX:PermSize=1024M -XX:MaxPermSize=2048m 了【JDK 1.7 以及以前的版本】,在进程启动的时候查看日志会有警告信息的,提示参数设置无效,会被忽略。

CopyOnWriteArrayList

占用内存,待整理。

模拟内存溢出

待整理。

进程已杀死问题

加大内存,机器内存不够分配,进程异常退出,待整理。

虾丸派 wechat
扫一扫添加博主,进技术交流群,共同学习进步
永不止步
0%