关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程

某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC。解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导。

1. 生成dump文件

为了定位FullGC的原因,首先需要获取heap dump文件,看下发生FullGC时堆内存的分配情况,定位可能出现问题的地方。

1. 1 通过JVM参数自动生成

可以在JVM参数中设置-XX:+ HeapDumpBeforeFullGC参数。
建议动态增加这个参数,直接在线上镜像中增加一方面是要重新打包发布,另一方面风险比较高

sudo -u admin /opt/taobao/java/bin/jinfo -flag +HeapDumpBeforeFullGC pid
sudo -u admin /opt/taobao/java/bin/jinfo -flag +HeapDumpAfterFullGC pid

也可以用HeapDumpOnOutOfMemoryError这个参数,只在outOfMemoryError发生时才dump。实测只有在fullgc完成时才会产生该文件,fullgc期间看不到。
此外还需要-XX:HeapDumpPath=/home/admin/logs/java.hprof这个参数来指定dump文件存放路径。

1.2 通过JDK工具生成

1.2.1 jmap

先获取java进程ID,再使用jmap进行dump。
注意,虚拟机上的jmap可能没有做路径映射,需要手动选择jdk路径下来执行

ps -aux | grep java
jmap -dump:file=test.hprof,format=b XXXX

1.2.2 通过jcmd

JDK7后新增的多功能命令,其中jcmd pid GC.heap_dump FILE_NAME的效果和jmap -dump:file=test.hprof,format=b pid一样。

1.3 JConsole

可以生成本机或远程JVM的dump。还有一些其他工具就不详细介绍了。

2. 下载dump文件

由于使用的是阿里云的服务器,可以直接将dump文件上传到OSS上通过公司内部工具来分析,或通过OSS再下载到本地。
设置OSSCMD:
操作命令 osscmd config --host=oss-cn-hangzhou-am101.aliyuncs.com --id=** --key=**
创建bucke:osscmd cb 000001
上传文件:osscmd put 1.txt oss://000001/
下载文件:osscmd get oss://000001/1.txt 1.txt

其他类型的Linux主机可以使用SCP命令,参考:Linux scp命令

3. 分析工具

通过dump文件来分析fullGC的原因,需要关注哪些类占用内存空间较多、不可到达类等。
由于使用的是公司内部工具Zprofiler和grace,详细的使用过程这里就不截图了。一些其他可用的工具和命令(参考Java内存泄漏分析系列之六:JVM Heap Dump(堆转储文件)的生成和MAT的使用):

  • jhat, JDK自带,使用jhat <heap-dump-file>生成网页,通过浏览器访问``查看
  • jvisualvm
  • Eclipse Memory Analyzer(MAT)
  • IBM Heap Analyzer

需要注意的是,只看dump文件有时还不能得到结论,因为占用空间大头的有可能是String、ArrayBlockingList这样的对象,而且内容可能是null或null对象的集合,无从排查。此时还要结合发生fullgc前后业务系统发生了什么动作来确定。如果有条件的话可以在日常环境或预发环境重现一下。
当然,如果内存中的空间消耗对象是特殊的类,就比较好排查了。

4. 分析和改进

具体情况具体分析。

4.1 本次排查的场景

查询DB中数据->在异步线程中通过poi转换成Excel->上传到OSS。

示例代码:

// 导出代码中将变量直接作为lambda表达式的值传入
List<XXData>  data = queryData(request);
SheetDownloadProperty property = sheetDownloadProperties.get(0);
property.setTotalCount(request.getQueryRequest().getPageSize());
property.setPageSize(request.getQueryRequest().getPageSize());
property.setQueryFunction((currentPage, pageSize) ->  data);
// 该组件会在线程池异步调用poi组件转换为excel、上传OSS、下载
asyncDownloadService.downloadFile(downloadTask);
private List<XXData> queryData(ExportRequest request) {
    //查询DB,略
}
// 查询方法
@FunctionalInterface
public interface PageFunction<T> {

    /**
     * 方法执行
     */
    List<T> apply(Integer currentPage,Integer pageSize);
}

4.2 dump文件分析

通过内部工具可见,fullGC前有三个占据内存较高的ArrayBlockingList,里面有大量的内容为null的Object。

这三个ArrayBlockingList所属的中间件,虽然本身和业务流程没有关系,但是仍不能排除嫌疑。

4.3 尝试解决

4.3.1 方案1:poi相关解决方案

由于依赖了二方库poi,这个库的usermodel模式很容易引起fullGC,同时也怀疑是因为lambda表达式直接传了变量。
把poi的usermodel改为事件模式(https://my.oschina.net/OutOfMemory/blog/1068972)可以避免这个问题。
但是该功能是一个二次封装的三方包中的,同时其他引用该组件的应用fullgc频率并不高,没有采用这个方案。

4.3.2 方案2:中间件升级

持有大量null对象的中间件版本较低,且新版目前已不再维护,老版本的releas note虽然没有提到这条bug fix,有一定嫌疑。
该中间件初始化时会创建三个容量为810241024的ArrayBlockingList,和dump文件相符合。
同样是因为这个中间件是在三方包中封装,不方便直接该版本,同样没有采用这个方案。

4.3.3 方案3:增大堆大小

可以调整metaspace参数来实现,本次想找到代码中相关的线索来解决,未采用该方案。

4.3.4 方案4:业务代码修改

仔细观察了这段代码在其他系统的的实现,发现其他系统的lambda表达式是匿名方法,而不是直接传值,即:

property.setQueryFunction((currentPage, pageSize) ->  {
    // 查询逻辑, 略
);

怀疑是直接传变量进去导致的垃圾回收问题。更改到这种模式后,触发下载功能时,连续长时间的fullGC仍然时有发生,没有解决问题。

4.3.5 方案5:替换垃圾回收器

暂时能确定的原因是,公司中间件本身占用堆内存较多,运行poi增加了GC的频率。但是由于它们都在二方库的原因,不方便修改。
此时搜索到stackoverflow有关于poi反复GC的一个问题,和我的情况类似,也是反复GC但是仍然不能释放内存。有回复建议将GC回收器替换为G1GC,将默认的UseConcMarkSweepGC替换后效果明显,一次FullGC就可以完成回收释放,不会反复FullGC,如下图,20:30前的fullGC是CMS,持续时间长且反复进行;20:30后是替换后第一次触发excel转换下载,进行了多次下载,即使发生FullGC也只有1次,大大缓解了之前的问题:

本次暂定只采用方案5。

G1GC在JDK9已替代CMS成为了正式的垃圾回收器,低版本JDK需要手动设置。具体需要设置的JVM参数:

-Xms32m
-Xmx1g
-XX:+UnlockExperimentalVMOptions
-XX:+UseG1GC
-XX:MaxHeapFreeRatio=15
-XX:MinHeapFreeRatio=5

注意前两行一般应用都会设置,不要覆盖掉。最后两行需要视情况调整。另外,默认的-XX:+UseConcMarkSweepGC需要去掉。

使用G1GC时需要确认工作线程数是否和预期一致,不要太多,一般来说和CPU核数一致即可。出现非预期数目的原因可能是,镜像脚本指定核数时,直接按照物理机而不是虚拟机核数来生成。
查看方式是看gc日志:

虚拟机设置核数的dokcker脚本示例:

export CPU_COUNT="$(grep -c 'cpu[0-9][0-9]*' /proc/stat)"

5. 其他

5.1 典型fullGC场景举例

  • 外部资源未释放,如将利用tair实现的分布式锁放在Map中,未做解锁
  • fastjson的反序列化异常抛出后没有处理
  • 框架固有缺陷,如本例apache的poi组件,使用usermodel模式做excel导出时,当操作比较频繁或有其他内存泄漏有可能造成
  • JVM的metaspace设置过小

5.2 core dump和heap dump

core dump是针对线程某一时刻的运行情况的,可以看到执行到哪个类哪个方法哪一行以及执行栈的;heap dump是针对内存某一时刻的分配情况的。

5.3 stackoverflow上关于poi内存占用问题的讨论:

简单摘译了一些,可以直接看原文。

  1. Java对堆内存分配是懒回收的,如果JVM不想这么做,即使运行Runtime.gc(),也可能什么也不做。sapiensl和Amongalen的回答
  2. 触发FullGC,并不是因为内存泄漏,仅仅是因为poi占用了太多的内存。Michael的回答

关于G1GC,会在后续文章中研究。

原文地址:https://www.cnblogs.com/wuyuegb2312/p/11799352.html

时间: 2024-10-14 13:14:30

关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程的相关文章

Apache POI组件操作Excel,制作报表(一)

Apache的POI组件是Java操作Microsoft Office办公套件的强大API,其中对Word,Excel和PowperPoint都有支持,当然使用较多的还是Excel,因为Word和PowerPoint用程序动态操作的应用较少.那么本文就结合POI来介绍一下操作Excel的方法.    Office 2007的文件结构完全不同于2003,所以对于两个版本的Office组件,POI有不同的处理API,分开使用即可.首先来说几个Excel的基本概念.对于一个Excel文件,这称为一个工

[转]线上GC故障解决过程记录

排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http://my.oschina.net/feichexia/blog/196575 背景说明 发生问题的系统部署在Unix上,发生问题前已经跑了两周多了. 其中我用到了Hadoop源码中的CountingBloomFilter,并将其修改成了线程安全的实现(详情见:AdjustedCountingBloo

一次线上GC故障解决过程记录

排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http://my.oschina.net/feichexia/blog/196575 背景说明 发生问题的系统部署在Unix上,发生问题前已经跑了两周多了. 其中我用到了Hadoop源码中的CountingBloomFilter,并将其修改成了线程安全的实现(详情见:AdjustedCountingBloo

线上性能问题初步排查方法

文章出处http://ifeve.com/find-bug-online/ 有时候有很多问题只有在线上或者预发环境才能发现,而线上又不能Debug,所以线上问题定位就只能看日志,系统状态和Dump线程,本文只是简单的介绍一些常用的工具,帮助定位线上问题. 问题定位 1: 首先使用TOP命令查看每个进程的情况,显示如下: top - 22:27:25 up 463 days, 12:46, 1 user, load average: 11.80, 12.19, 11.79 Tasks: 113 t

性能测试之线上引流测试--让性能测试更真实更丰富

为什么要做引流测试 目前为止大部分的测试是在测试环境下,通过模拟用户的行为来对系统进行验证,包括功能以及性能.在这个过程中,你可能会遇到以下问题: 用户访问行为比较复杂,模拟很难和用户行为一致,模拟不够真实; 线下模拟场景有限,会出现业务覆盖不全的情况.引流测试就是为了解决以上问题,通过把线上的真实流量复制到线下环境,解决测试环境模拟不够真实,或覆盖不够全面的问题. 引流的做法 目前不少公司对引流测试进行了实践,主要有以下4种引流方式: 以上几种办法各有利弊,有的是需要自己开发相应的工具来支持.

第二代支付系统及电票线上清算相关知识

二代支付系统 国家处理中心(NPC):NPC是人民银行连接支付系统所有城市节点和特许参与者的中枢节点,负责接收.转发各CCPC和接收.处理特许参与者的支付指令,以及资金清算的一组硬件和软件系统的总称.参加支付系统的直接参与者必须在人民银行国家处理中心开设清算账户. 城市处理中心(CCPC):CCPC是人民银行支付系统的城市节点,连接NPC和各直接参与者,负责在NPC和直接参与者之间接收和转发支付指令的一组硬件和软件系统的总称. 人民银行二代支付系统  CNAPS(China National A

线上问题排查

线上操作与线上问题排查实战 技术同学需要经常登录线上的服务器进行操作,58到家架构部/运维部/58速运技术部,联合进行了一次线上操作与线上问题排查实战演练,同学们反馈有收获,特将实战演练的问题和答案公布出来,希望对大家也有帮助. 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服务各种连接状态(TIME_WAIT/ CLOSE_WAIT/ ESTABLISHED)的连接数. 参考答案: netstat -n | grep 1.2.3.4:2

从线下走到线上,艺术品能否找到新突破口?

艺术品因其具有的收藏价值.文化价值.观赏价值以及不可复制性,让收藏者愿不远万里前去赴约,许以千金只求抱得珍品归.中国作为有五千年悠久历史的古国之一,前人留下的传世艺术品不计其数,艺术品交易需求不小,但是传统的线下艺术品交易方式存在着许多弊端. 传统线下艺术品交易,不管是画廊还是拍卖的形式都受到了地域和空间的限制,知名画廊或拍卖机构大多集中在一.二线城市,譬如北京著名的798艺术区,国内外消费者在购买艺术品时都要长途奔波,耗费购买之外的时间和费用.此外,由于线下画廊属于私下交易,成交价格保密,传统

使用tcpcopy导入线上流量进行功能和压力测试

- 假设我们要上线一个两年内不会宕机的先进架构.在上线前,免不了单元测试,功能测试,还有使用ab,webbench等等进行压力测试. 但这些步骤非生产环境下正式用户的行为.或许你会想到灰度上线,但毕竟可能会影响到部分用户,这怎么对得起我们两年内不宕机的承诺呢? 好在网易的 王斌 开发了tcpcopy, 可以导入线上流量进行功能和压力测试. tcpcopy介绍 tcpcopy是一种请求复制工具.可以将线上流量拷贝到测试机器,实时的模拟线上环境.在不影响线上用户的情况下,使用线上流量进行测试,以尽早