问题
在上一篇博文【内存泄漏】一个现网问题告诉你血淋淋的事实:java内存泄漏很严重处理了由于打开资源后没有及时释放,导致内存使用率居高不下的问题(35.80%降至4.6%),但是CPU使用率依然很高。见下图。

问题发现过程
- 查看那个线程占用CPU高
使用top -Hp [PID]命令查看使用CPU过高的进程的CPU和内存使用情况,发现CPU时间片大部分被VM Thread线程使用了,此刻真是一脸懵逼,这个是JVM的线程,为什么会有这么高的使用率。

- VM Thread是做什么的
查阅网上的资料,了解到VM Thread线程等待需要JVM到达安全点的操作出现。这些操作必须在单独的线程上进行的原因是,这些线程都要求JVM处于一个安全点,在安全点上不会对堆进行修改。该线程执行的操作类型为“stop-the-world垃圾收集、线程堆栈转储、线程暂停和有偏锁定撤销。
参考材料:

从上面信息看,CPU使用率高是因为这个线程在GC。
- 查看进程的GC情况
通过jstat -gc [pid] [interval] [count]查看查看当前进程GC情况,其中interval为1000,发现YGC的次数一直增加,大概1s一次。怀疑是我们代码中临时变量,导致了年轻代的垃圾回收。
- 查看线程运行情况
由于内存使用率一直很高,没有闲时和忙时的区别,为了排除干扰,在系统闲时使用命令jstack -l [PID]对进程进行线程快照,重复多次快照后,发现有一种线程在闲时也多次出现,基本上是每秒一次。

查阅对应代码,这个方法是用于解密从数据库中返回的敏感信息,用于展示用户信息的明文展示。在分页查询时,不会有太多的问题,但是发现,由于接口的滥用,这个接口也用于系统内模块间的用户信息同步,并且还是高频调用(一秒一次),在系统用户是XX万时,每秒读取一次全部用户信息,会产生大量的临时变量,从而导致了高频的年轻代GC。
问题解决
开发一个模块间同步信息的接口,并将用户裁剪,只保留关键信息至缓存,30s从数据库同步一次。减少从数据库查询及解密的次数,减少对临时变量的使用。VM Thread CPU时间累积使用率从33.7%迅速降了下来。

文章描述了一次解决Java应用中CPU使用率高问题的过程。通过分析发现VMThread的高CPU使用与频繁的YGC有关,原因是代码中大量临时变量导致的年轻代垃圾回收。最终,通过优化接口使用,减少数据库查询和解密操作,将CPU使用率显著降低。
1764

被折叠的 条评论
为什么被折叠?



