java大对象引发的频繁full gc及cpu飙高的调优历程
问题描述linux系统,执行top命令-bash-4.2# toptop - 08:57:47 up 147 days, 23:08, 2 users, load average: 67.32, 60.83, 58.97Tasks: 1310 total,2 running, 1307 sleeping,0 stopped,1 zombie%Cpu(s): ...
问题描述
linux系统,执行top命令
-bash-4.2# top
top - 08:57:47 up 147 days, 23:08, 2 users, load average: 67.32, 60.83, 58.97
Tasks: 1310 total, 2 running, 1307 sleeping, 0 stopped, 1 zombie
%Cpu(s): 48.9 us, 1.2 sy, 0.0 ni, 49.6 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 56109344+total, 1076992 free, 49768768 used, 51024771+buff/cache
KiB Swap: 0 total, 0 free, 0 used. 50798355+avail MemPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
70853 root 20 0 71.947g 0.031t 20088 S 6978 6.0 11566:28 java
30632 root 20 0 43.589g 1.229g 12108 S 68.9 0.2 9335:55 java
2135 root 20 0 160600 46492 832 R 13.7 0.0 4648:46 statmon
4219 root 20 0 99.287g 2.030g 11576 S 2.9 0.4 747:32.73 java
59325 root 20 0 158752 3552 1588 R 2.5 0.0 0:00.70 top
57806 root 0 -20 0 0 0 S 1.9 0.0 0:01.70 kworker/18:2H
10 root 20 0 0 0 0 S 0.6 0.0 313:00.57 rcu_sched
129997 root 20 0 44.030g 3.102g 11992 S 0.6 0.6 2840:59 java
上述70853这个进程,占用过高的cpu,而且程序运行缓慢,重启之后就正常了,运行一段时间之后又是这样。
问题排查
线程使用cpu排查
首先遇到cpu彪高的问题,我们要先定位是哪些线程在持有这些cpu,采用top -Hp pid命令可以查看线程的cpu使用情况
-bash-4.2# top -Hp 70853
top - 09:03:01 up 147 days, 23:13, 2 users, load average: 39.88, 48.75, 54.03
Threads: 601 total, 93 running, 508 sleeping, 0 stopped, 0 zombie
%Cpu(s): 43.2 us, 11.3 sy, 0.0 ni, 45.0 id, 0.3 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 56109344+total, 878212 free, 49768532 used, 51044672+buff/cache
KiB Swap: 0 total, 0 free, 0 used. 50798249+avail MemPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
70866 root 20 0 71.948g 0.032t 20104 R 80.4 6.0 79:06.89 java
70906 root 20 0 71.948g 0.032t 20104 R 80.4 6.0 78:58.43 java
70921 root 20 0 71.948g 0.032t 20104 R 80.4 6.0 78:58.79 java
70856 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:01.84 java
70859 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 78:57.72 java
70863 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:06.32 java
70865 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:09.23 java
70872 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 78:52.49 java
70875 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:02.85 java
70877 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:04.04 java
70879 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:07.52 java
70881 root 20 0 71.948g 0.032t 20104 R 80.1 6.0 79:00.38 java
上述是一系列的该进程下的线程使用cpu的情况,并且省略了一部分,将上述pid下面的十进制的线程id转成16进制,如下:
-bash-4.2# printf "%x\n" 70856
114c8
线程堆栈排查
上述排查到了哪些线程使用cpu率高,那么这些cpu到底在干什么呢?可以使用jstack pid命令查看jvm的线程堆栈
gc动态监控
上述线程堆栈查看,发现线程大部分在执行gc回收,这就奇怪了,为什么一直在gc,具体查看gc的情况,可以使用jstat -gcutil pid
-bash-4.2# jstat -gcutil 70853 1000
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 99.90 99.98 92.68 88.47 11534 1351.766 2466 4811.444 6163.210
0.00 0.00 99.99 100.00 92.68 88.47 11534 1351.766 2467 4813.394 6165.160
0.00 0.00 99.99 100.00 92.68 88.47 11534 1351.766 2467 4813.394 6165.160
0.00 0.00 99.96 100.00 92.68 88.47 11534 1351.766 2468 4815.426 6167.192
0.00 0.00 99.96 100.00 92.68 88.47 11534 1351.766 2468 4815.426 6167.192
0.00 0.00 100.00 99.99 92.68 88.47 11534 1351.766 2469 4817.455 6169.221
0.00 0.00 100.00 99.99 92.68 88.47 11534 1351.766 2469 4817.455 6169.221
0.00 0.00 99.96 99.99 92.68 88.47 11534 1351.766 2470 4819.330 6171.096
0.00 0.00 99.96 99.99 92.68 88.47 11534 1351.766 2470 4819.330 6171.096
0.00 0.00 100.00 100.00 92.68 88.47 11534 1351.766 2471 4821.328 6173.094
0.00 0.00 100.00 100.00 92.68 88.47 11534 1351.766 2471 4821.328 6173.094
0.00 0.00 99.99 100.00 92.68 88.47 11534 1351.766 2472 4823.229 6174.995
0.00 0.00 99.99 100.00 92.68 88.47 11534 1351.766 2472 4823.229 6174.995
0.00 0.00 99.97 99.97 92.68 88.47 11534 1351.766 2473 4825.121 6176.888
0.00 0.00 99.97 99.97 92.68 88.47 11534 1351.766 2473 4825.121 6176.888
0.00 0.00 100.00 99.99 92.68 88.47 11534 1351.766 2474 4827.092 6178.859
0.00 0.00 100.00 99.99 92.68 88.47 11534 1351.766 2474 4827.092 6178.859
0.00 0.00 99.96 99.99 92.68 88.47 11534 1351.766 2475 4829.041 6180.807
0.00 0.00 99.96 99.99 92.68 88.47 11534 1351.766 2475 4829.041 6180.807S0,S1:新生代的两个幸存者区
E:新生代的伊甸园区
O:老年代
YGC:新生代gc
FGC:full gc
上述主要指标解释
S0,S1:新生代的两个幸存者区
E:新生代的伊甸园区
O:老年代
YGC:新生代gc
FGC:full gc
通过上述排查,可以发现,一直在full gc,并且老年代内存并没有回收,致此,内存泄露了。
堆内存查看占用内存的对象
采用jvisiual命令查看heap dump
上图所示被ScheduledThreadPoolExecutor中的优先级队列持有。
代码分析
逻辑是这样的,视图库接收前端实卡传来的图片,高清图片,大概有几百kb到十几M不等,采用1400标准接口,内部采用线程池来异步处理传来的图片及结构化数据,线程池代码如下所示:
private ScheduledExecutorService threadPool ;
private BasicService basicService;
@PostConstruct
public void vcnLogin(){
BasicService basicService = new BasicServiceImpl();
basicService.setLogPath("/opt/");
int result = basicService.initial();
int result1 = basicService.login("vcnesdk02", "Huawei12#$", "34.8.20.132", 9900);
logger.info("vcn login result: "+result1);threadPool = Executors.newScheduledThreadPool(50);
}
来的图ScheduledThreadPoolExecutor线程池默认采用DelayedWorkQueue,是一种延时的优先级队列,在这里需要关注的是无界队列。
项目刚开始运行没什么问题,在早高峰和晚高峰的时候,数据突然多了起来,full gc发生频繁,因为采用的并行GC算法,占用资源过多,导致线程池处理越来越慢,队列积压的任务越来越多,老年代空间越来越小,造成full gc越来越频繁,gc越频繁,任务处理越慢,最终导致上图所示的老年代都满了,但是full gc之后,空间一直不下降。数据接入曲线图如下:
图中显示早高峰7点半的时候,达到了数据洪峰,系统扛不住了,于是老年代满了,頻繁full gc,导致系统越来越慢,吞吐量下降。
解决方案
略,随意。
更多推荐
所有评论(0)