服务器报警:内存超95%
服务器报警:内存超95%
背景:
收到阿里云监控报警:服务器内存占用太高
,
和运维同事沟通了解后 这台机器上运行着另外一家公司的java项目, 这个项目启动后,内存会逐渐升高,直到95%就不变了,一直居高不下,这个问题已经很长时间了,由于涉及另外一家公司,而且似乎项目运行功能也没问题,只是多占些服务器资源,就一直没排查。
本着好奇的态度,我决定查查怎么回事
排查步骤:
- top发现java进场内存使用率比较高,首先想到的是看日志,但是并没有OOM的记录
- 按照一般的想法,还是要导出dump文件,看看内存里都是什么对象, 之前比较熟练是 用arthas导出
https://arthas.aliyun.com/doc/
有些命令记不清,每次用arthas都是到这个网页去找。
遇到一个问题:
[arthas@58205]$ heapdump arthas-output/dump.hprof
Dumping heap to arthas-output/dump.hprof ...
Heap dump file created
执行后找不到文件去哪儿了,查了半天发现 arthas-output 是 自动在tomcat的bin目录下
dump.hprof文件比较大 4G, 压缩tar -vczf 后下载下来
3.用MAT打开dump.hprof文件,打开dominator_tree
发现最大是 java.lang.ref.Finalizer ,再点开 发现它好像是个链表结构,一直next
既然是链表结构,它节点本身是什么呢,看到referent 是 java.util.zip.ZipFile
查看ZipFile的outgoing
发现是arthas的jar,肯定是找错了。 又查了几个next 还是 arthas的jar, 肯定不是arthas引起的,那么到底是什么引起的呢 。 这个Finalizer 这么大,有没有办法统计出每个next里分别是什么对象呢, 去网上查资料,终于找到一个类似的帖子:
https://blog.csdn.net/shy_1023/article/details/135238034
帖子里提到可以用 OQL 查询
SELECT z.clazzName AS clazzName, z.maps AS Maps, z.maps.@length AS Count
FROM OBJECTS ( eval((SELECT s.sz AS clazzName, (SELECT OBJECTS m FROM java.lang.ref.Finalizer m WHERE (toString(m.referent.@clazz.@name) = s.sz)) AS maps FROM OBJECTS ( SELECT DISTINCT toString(h.referent.@clazz.@name) AS sz FROM java.lang.ref.Finalizer h ) s )) ) z
按F5执行发现 最多的是SocksSocketImpl
通过SocksSocketImpl incoming outgoing 一顿找 (incoming 是看哪些对象包含当前对象, outgoing是看 当前对象包含什么),终于找到了认识的 jdbc
再往下找就找不到了。
4.然后就继续在网上找资料,终于找到了一个帖子:
https://blog.csdn.net/MakeContral/article/details/76615377
https://blog.csdn.net/u014365523/article/details/127513012
同时反编译了下 对方的代码,发现数据库连接没有使用线程池,里面有定时任务,1分钟执行一次, 一次里面有多个数据库查询操作, 每个查询总是新建数据库连接 关闭连接
大概理解的意思是: jdbc 连接 有重写 finalize() 方法,这导致资源释放不及时,好多对象被 java.lang.ref.Finalizer 引用, jvm不会立马杀掉它。
中间还尝试了 jcmd命令, dashborard命令
5.另外发现似乎内存升高和 超大文件的IO有关系,沿着这个方向找也没查到特别有价值的资料。
解决方式:
跟踪了两天还是没有完全搞清楚原因,先尝试修改JVM参数,发现之前用的CMS收集器, 改为G1 收集器 -XX:+UseG1GC,据说G1 回收效率比较高,另外加了-XX:MaxDirectMemorySize=200m 防止使用Direct 内存太多。后续有时间再跟踪。
总结:
虽然没有彻底解决问题,但是通过这次跟踪 还是学到了一些知识:
- OQL的作用,之前只是知道并没有真正用过
- Finalizer 的原理, 链式结构,GC慢
- 理解了 数据库连接池化的好处
- JVM的一些参数