记录一次生产问题排查

现象

生产一个导入导出服务频繁掉线,看监控指标发现GC频繁,应用内存快要达到上限。怀疑有业务代码有大内存问题,或者客户大数据操作。

紧急处理方式

调大一倍JVM内存,看下是否能跑过此业务。

分析

  1. top -H看到进程内部线程PID28665的线程占用CPU高

    PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    28665 tomcat    20   0 8680236 4.753g  17396 R 99.9 30.3
  2. 转换成16进制是6ff9

    $ printf %x 28665
    6ff9%
  3. jstack查找nid=0x6ff9线程是神秘

    "TaskExecutor-Run-23" #539 prio=5 os_prio=0 tid=0x00007f2ce409d800 nid=0x6ff9 runnable [0x00007f2c68ea3000]
      java.lang.Thread.State: RUNNABLE
      at java.util.ArrayList.indexOf(ArrayList.java:316)
      at org.apache.poi.xssf.model.StylesTable.putStyle(StylesTable.java:247)
      at org.apache.poi.xssf.usermodel.XSSFCell.setCellStyle(XSSFCell.java:502)
      /// 忽略业务代码
  4. 从连续的jstack & top -H中发现,都是此业务代码占用cpu,从内存dump里面发现,大内存占用也是此业务方法的线程

  5. 查看业务代码发现业务代码的POI写法有问题

    SXSSFWorkbook book = new SXSSFWorkbook(new XSSFWorkbook(inputStream), 500);
    Sheet sheet0 = book.getXSSFWorkbook().getSheetAt(0);
    // 省略对sheet0的操作
    // book.getSheetAt(0) 改成这样,业务上用SXSSFWorkbook能够满足。

    这样实际上拿到的是XSSFWorkbook,并没有使用流式的SXSSFWorkbook,这样所有的数据都会加载到内存里面,导致内存飙升。 内存飙升导致GC频繁,GC暂停时间过长,触发断路器断路,从而触发断路告警。