最近正在开心的搞一些事情,然后就搞出了事情。。。 因为公司项目需要,需要借助java agent 来完成一些操作,然后在测试环境接入后,2小时+java进程就消失了。 哥们,你去哪里了?
来排查下吧
先查看下message吧
既然是linux,一般如果进程是被kill的话,var/log/messages,应该会有相关kill 日志,但是这里并没有看到什么信息。这里我们是虚拟机或容器可能这里的信息不准确?
是不是有些东西回收不掉
来看下java 的gc情况,启动后持续观察了一段时间
在来看下当前jvm内存相关信息(jmap一下)
28295 503 20 0 7047m 2.3g 19m S 1.3 61.4 43:16.25 java
看下启动参数配置 为 -Xmx2048M -Xms2048M -Xmn768M, 这里已经超出了设置的最大堆2G,不过加上线程什么的占用还算合理,
看下当前系统空闲内存
已经没有太多了,如果后续当前机器上有消耗内存的操作等(比如vim大文件)那么系统很有可能kill掉此进程,难道就是因为我们机器内存不足,先设置线程占用小一点
-Xmx1024M -Xms1024M -Xmn512M
启动后RES依然会升高到2G,并没有能够限制住内存占用。那么看来应该是哪里用了堆外内存了?
堆外内存排查
配置了jvm参数 -XX:NativeMemoryTracking=detail
jmap -heap pid:查看堆使用情况,这里没做截图,看起来也都正常的。那到底被什么占用了呢?
这里java提供的相关工具看起来都是正常的。
只能借助linux的相关工具了
pmap来看下
pmap查看内存分布,发现大量的64M的地址(pmap -x pid),没留图,网上找的图片,
这个64m是什么?也找了一些资料,确实有人碰到类似的情况。 blog.csdn.net/qianshangdi…
这里也尝试调整gcc等相关版本什么的,依然没有什么效果,这被什么东西给吃了呢。 黔驴技穷了,有小伙伴说要不试试google的神器gperftools。
goerftools搞一下
配置完成后可以看到大概分配到1.3G的时候可以回收,当前占用是923MB(这里感觉是到了-Xmx设置的最大限制,所以开始回收)
可以看到这里有一个比较大的增长,1402MB->3392MB
比较了下前后两个内存的占用,这里当前dump的总共923,看到占用最多的是updatewindow,自己就占用了727.
看起来是jetty在调用?jetty你在悄悄的做什么?调用这个为啥就会有问题呢?
jetty的bug吗?
看了下jetty的github,有这么一个issues github.com/eclipse/jet… 大概是jetty为了支持Servlet3.0规范里的注解,比如@WebServlet,会扫描当前项目依赖的jar包,找到标注对应注解的类,来加载为servlet操作、这里用到了jdk的提供的gzip解压缩类,这里jdk并没有对使用到的堆外占用进行释放。
jetty大佬说这不是jetty的问题,是jdk8的bug,建议升级jdk(还是个酷酷的妹子?),说是想和oracle讨论,但是没找到啥渠道,测试1.9不再存在这个问题
bugs.java.com/bugdatabase… Bug), 大佬也给了一些解决方案
这里我们升级jdk9,全公司升级,太难了。。,上面的方案看起来是禁用相关的缓存,测试后并没有解决。
这里翻了下jdk的版本日志,也没有找到修复此问题的版本说明,直接升级到jdk1.8最新版本,问题依然存在(RES依然很高)。
又搜索了一番,看有大佬提议,那么既然是因为jetty支持servlet3.0的注解规范。我们一般没有使用到,给他去掉呗?
查了下jetty相关资料,jetty的这个支持是在jetty-annotations.xml里面配置的,来去掉试下。
启动后观察,之前RES始终会升高到2.3G以上,现在稳定在了780m。看起来解决了。
来总结下
-
问题原因 jetty会使用jdk8的gzip 操作,这里jdk8会有个bug造成占用的堆外信息不回释放。这里jetty主要用来扫描当前工程依赖的jar包,如果不是很多,内存够用,项目正常启动扫描完成后,如果机器内存依然很富裕,不会有宕机风险的。
而我们这里启动后可用内存显然已经不足,或是堪堪够用,但是一些其他操作,例如vim大文件造成内存抖动,造成可使用内存过少触发系统保护,进程被kill。
-
解决方案
这里其实jdk9已经修复了此bug,可以升级到jdk9。 或者去除jetty对servlet3.0的支持。当然你也可以加大机器的内存,当jetty扫描完成jar包依赖,内存虽然占用很高,但是依然是有峰值的,不会无限制升高。