問題描述
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,導致系統越來越慢,吞吐量下降。
解決方案
略,隨意。