Java程序持续Full GC的处理经历

1.发现问题

最近在检查爬虫程序运行情况的时候发现采集速度非常慢,美国空间,查看cpu占用率的时候发现有一个内核一直是100%,按理说多线程的程序不应该会出现这样的情况。第一反应就是用jstack -l [pid]把线程dump出来查看栈情况,但没发现什么异常。然后就考虑用jmap -heap [pid]把内存使用情况打印出来看看,奇怪的是出现好几次连接不上的情况,等打印出来发现居然旧生代已经满了。把内存调大,发现过不了一会又是这样,确实非常蹊跷。

2. 深入调查

看来问题比较棘手,只好用更细致的方式去分析了。一方面打开了GC日志,同时在运行时用jstat -gcutil [pid] 1000来跟踪GC情况。现象很奇怪,旧生代空间缓慢增长,但突然新生代内存占用100%,旧生代紧接着也变成100%,虚拟主机,然后就一直处于full gc,但内存丝毫没有减少,整个过程持续了5分钟。(截图忘记保存了,悲剧…)

看现象就是对象爆炸,但程序已经运行很久了,如果有这么严重的bug应该早就发现了。严谨起见,我用jmap -histo [pid]把程序的对象情况打印出来,香港服务器,结果非常惊讶。

系统中居然出现那么多ParserError对象,这个是程序中用到的一个Jsoup开源包里面的东西。看命名像是一个Exception,但查看源码时才发现居然是一个Class。至于这个东西从哪里蹦出来的呢?!我在现象出现的时候打印了下线程栈信息,果然定位到了生成这个对象的那个位置。

在源码中找到相应的实现,主要是调用了这样一个方法:

这里有一个trackErrors做开关,默认是true,但找了一下,居然没有最外层的方法去控制它,而errors这个对象也是一个private值,没有任何调用,可能是作者自己测试用的。

3. 问题的处理

原因调查清楚,处理就很简单了。直接把源码中trackErrors的默认值改成false,再确认了一下其他相关调用的方法,然后重新编译打包,把原始包替换了。

4. 总结

找问题还是要先分析出可能的原因,然后借用工具去定位问题,很多时候数据比经验更可靠。

我们摇摇头说,困难其实没什么大不了。

Java程序持续Full GC的处理经历

相关文章:

你感兴趣的文章:

标签云: