zoukankan      html  css  js  c++  java
  • VisualVM 分析full GC问题记录

    背景:JAVA APP,主要功能是处理日志并存入db

    现象:运行一段时间就出现OOM问题,查看GC log发现运行没多久就一直Full GC,并且抛出OOM的异常。

    [Full GC (Ergonomics) [PSYoungGen: 529920K->525999K(614912K)] [ParOldGen: 1398052K->1397869K(1398272K)] 1927972K->1923868K(2013184K), [Metaspace: 33827K->33827K(1079296K)], 4.1812153 secs] [Times: user=32.38 sys=0.07, real=4.19 secs]
    [Full GC (Ergonomics) [PSYoungGen: 529920K->525483K(614912K)] [ParOldGen: 1397869K->1397848K(1398272K)] 1927789K->1923331K(2013184K), [Metaspace: 33832K->33832K(1079296K)], 5.6714054 secs] [Times: user=43.50 sys=0.09, real=5.67 secs]

    GC日志中老年代非常大,而且回收效果也不明显。遇到这种问题,第一感觉还是有内存泄露,虽然Java能自己回收内存,但是不能保证我自己写的程序没有问题,比如曾经犯过一个错误往list一直add object,却没有remove,并且list也不释放,导致list越来越大,最后内存OOM。本着大胆假设,小心求证的理念,开始排查问题。

    遇到内存问题,最好是希望能够直观的看到Java程序堆中现在有哪些对象,有哪些对象数目一直在递增而没有被回收。为此需要借助工具来排查了,visualVM是非常好的能满足需求的一个工具。

    启动visualVM,启动程序,通过Visual GC查看内存情况。

    从上图中,我们很明显发现程序的Old Generation占用空间是在不断地上涨,并且没有明显下跌,说明生成的对象都是存在内存里面的,并没有被释放掉。接下来查看一下内存中有哪些对象

    使用Sampler标签页中的Memory 采集功能,查看内存中当前是哪些对象,从采样结果来看Request对象是非常多的,而且一直在递增。Request对象在程序中使用较多,接下来看下是哪个线程一直在积压Request

    查看Per thread allocations,很容易看出insertRequestList线程分配的内存最多。

    对照程序我恍然大悟,发现问题就出在插入数据上。程序中插入数据库采用异步批量插入方式,当生成一个Request对象就存入一个LinkedBlockingQueue,集满一定量进行批量插入。而我设置的LinkedBlockingQueue初始化了一个非常大的capacity,这就导致来不及插入的Request全部都堆积在LinkedBlockingQueue中了。

    临时解决方案:将LinkedBlockingQueue的capacity设置小一些,并且采用put方式插入Request到LinkedBlockingQueue,当插入来不及的时候就等待。

    修改代码后再来看下visualVM结果:

    old Generation每次GC能回收大量的Object,不会出现Full GC了。

    Request对象数量也并不是一直在增长了。但是InsertRequestThread线程仍然是分配了很多的内存,可以得知该线程必然还是积压了大量的Request对象的。因为只采用了临时解决方案,mysql插入数据速度上不去,积压在所难免。

    附录:

    visualVM远程监控

    在执行Java 应用程序的服务器上先生成一个jstatd.all.policy

    grant codebase "file:${JAVA_HOME}/lib/tools.jar" {
       permission java.security.AllPermission;
    };

    然后执行命令

    nohup ${JAVA_HOME}/bin/jstatd -J-Djava.rmi.server.hostname=10.111.123.234 -J-Djava.security.policy=jstatd.all.policy -J-Dcom.sun.management.jmxremote.authenticate=false -J-Dcom.sun.management.jmxremote.ssl=false -J-Dcom.sun.management.jmxremote.port=8888 &  

    然后在本地启动visualVM,Remote添加10.111.123.234,默认端口1099就可以监控服务器上的程序运行状况了,非常方便。

    如果出现access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses" "write")报警,则建议将$JAVA_HOME和jstatd.all.policy都使用绝对路径。

  • 相关阅读:
    mysql常用基本命令
    mysql8.0.13下载与安装图文教程
    k8s ingress 增加跨域配置
    Jenkins 备份恢复插件 thinBackup 使用
    k8s HA master 节点宕机修复
    nginx 跨域问题解决
    mongodb 3.4.24 主从复制
    k8s 线上安装 jenkins并结合 jenkinsfile 实现 helm 自动化部署
    k8s helm 运用与自建helm仓库chartmuseum
    centos6 源码安装 unzip
  • 原文地址:https://www.cnblogs.com/snowater/p/7875335.html
Copyright © 2011-2022 走看看