qq_47988411 2021-06-28 16:41 采纳率: 0%
浏览 196

java大字符串频繁gc 导致系统挂掉怎么解决

前端传入大字符串  进行加密 导致字符串常量池中内存占用过大   频繁gc 导致系统挂掉怎么解决

  • 写回答

1条回答 默认 最新

  • 小P聊技术 2021-06-28 16:50
    关注

    java大对象引发的频繁full gc及cpu飙高的调优历程

    问题描述

    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 Mem 
    
       PID 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 Mem 
    
       PID 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.807 
    
    S0,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,导致系统越来越慢,吞吐量下降。

    解决方案

     

    评论

报告相同问题?

悬赏问题

  • ¥15 react-diff-viewer组件,如何解决数据量过大卡顿问题
  • ¥20 遥感植被物候指数空间分布图制作
  • ¥15 安装了xlrd库但是import不了…
  • ¥20 Github上传代码没有contribution和activity记录
  • ¥20 SNETCracker
  • ¥15 数学建模大赛交通流量控制
  • ¥15 为什么我安装了open3d但是在调用的时候没有报错但是什么都没有发生呢
  • ¥50 paddleocr最下面一行似乎无法识别
  • ¥15 求某类社交网络数据集
  • ¥15 靶向捕获探针方法/参考文献