线上问题解决:java之full gc问题分析,定位及解决

2021-03-17 14:51:48 浏览数 (1)

结合生产环境的一些案例,可能有些案例,你知道了,或者是听说过,可能你还不了解,一起说下,过下生产中的问题。

full gc
  • ① 启动参数配置

-Xms2g -Xmx2g JVM堆内存的最小值,最大值设置成相等的。

预热过程(堆内存小于最小值),慢慢的增长到最小值,而不是直接打到最小值的2g,如果需要直接像操作系统申请足够的内存,请在参数中增加:-XX: AlwaysPreTouch,直接申请2g的内存。

  • ② GC

了解GC的配置,而不是需要了解GC代码是怎么写,其实就是熟悉JVM的应用,还有JVM的更新信息,一定要跟着官网走肯定是没有错的。要掌握的是梳理的方式。很多事情不是出了问题去解决,很多时候是提前的预防工作。

线上的系统:GC的并行 (-XX: UseParNewGC) CMS机制(用户线程和GC线程同步),官方建议是G1,不久将来肯定都是G1。

提前的预防:日志不能缺少。不要stop the world,运行过程中犹豫GC导致的停顿,暂停,java代码运行不了,需要一定的时间进行垃圾回收。

打印gc的信息,filepath是日志的路径。

代码语言:javascript复制
-verbose:gc -XX: PrintGCDetails -Xloggc:filepath -XX: HeapDumpOnOutOfMemoryError
  • ② 代码写作错误

正常的情况下,不需要控制gc,依赖操作系统的gc就够了,但是代码里面却写了System.gc()。导致程序不稳定。写这个比较头疼,不像写其他的异常,在高并发场景或者特殊场景下面,应用暂停,应用无法访问,如何排查,首先需要了解,那些情况会出现full gc的情况,内存不够用固然是一个方面,很多时候代码写的有问题。

下面这段代码,本身设置512m的内存,结果每次申请256M的内存,明显内存不够,进行了gc。还有个gc就是代码里面自带的gc情况。

代码语言:javascript复制
// 频繁调用system.gc导致fullgc次数过多
// 使用server模式运行 开启GC日志
// -Xmx512m -server -verbose:gc -XX: PrintGCDetails -XX: HeapDumpOnOutOfMemoryError
public class FullGCDemo1 {
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i  ) {
            byte[] tmp = new byte[1024 * 1024 * 256]; // 256兆
            System.gc(); // 8G堆 128兆。full GC
            System.out.println("我GC一次了");
            Thread.sleep(2000L);
        }
    }
}
  • ③ 案例分析

一个服务器部署了一个应用,这个应用有很多的功能,其中有个execl的表格读取。用到了一个jar,JXL。

pom.xml

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study.jvm</groupId>
    <artifactId>om-demo</artifactId>
    <version>1.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.sourceforge.jexcelapi</groupId>
            <artifactId>jxl</artifactId>
            <version>2.6.12</version>
        </dependency>
        <!-- 自动下不了,就手动添加到自己的maven本地仓库 -->
        <!-- https://mvnrepository.com/artifact/com.dianping.cat/cat-client -->
        <dependency>
            <groupId>com.dianping.cat</groupId>
            <artifactId>cat-client</artifactId>
            <version>3.0.0</version>
        </dependency>

    </dependencies>

</project>
代码语言:javascript复制
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;

import java.io.File;

// 使用server模式运行 开启GC日志
// -Xmx512m -server -verbose:gc -XX: PrintGCDetails
// 很多人都会建议的规避System.gc带来的fullgc风险  -XX: DisableExplicitGC 禁止程序显示调用gc方法
public class FullGCDemo2 {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 1000; i  ) {

            Workbook book = Workbook.getWorkbook(new File(FullGCDemo2.class.getClassLoader().getResource("FullGCDemo2.xls").getFile()));
            // 获得第一个工作表对象
            Sheet sheet = book.getSheet(0);
            // 得到第一列第一行的单元格
            Cell cell1 = sheet.getCell(0, 0);
            String result = cell1.getContents();
            System.out.println(result);
            book.close(); // 第三方依赖包,内部可能适用了system.gc()
            Thread.sleep(2000L);
        }
    }
}

resources 目录下 添加一个excel文件

excel文件

正常情况下没有任何的报错

加入gc的监控看看,发现了很多System.gc();

检查发现源码中存在Sytem.gc() ,本以为开源代码很健全谁料,这么多坑,真是防不胜防啊,怎么办?

其实别人在写开源的代码都想到了。仔细看里面的源码发现。

再次运行,发现没有full gc的情况了。

总结下,可能这一个框架有可以禁用gc,一个大项目用了很多框架我不可能每个都看看源码吧,所以需要提前做性能的测试,发现里面的System.gc();解决他。但是有可能有的框架本身必须需要进行gc,例如大数据这块。其实这就是调优去衡量,那些需要gc,那些不需要gc。太极玩的就是一个平衡,并不是这个事情多严重,但是这个坑一定得注意,这就是性能测试的必要性。小功能的jar自带的gc,可能导致这个系统的gc,本来10秒一次的gc,因为这个小功能1秒中gc一次,导致整个系统也是1秒1个次,本身开发系统就是为了减少耦合。针对这些问题应该如何解决。dubbo这个框架里面引用了spring框架,其实根本没有引用里面的jar,而是把里面的源码放到dubbo的一个包下面,做定制的开发,这样我可以控制gc了。这就可以完全避免耦合性,因为代码都是我的,我可以随意的控制。一定要检查第三方包的使用情况。

在jdk很多源码中都存在 //help GC,其实就是first =null 这样就释放了,gc就自动的回收了。针对文件导入excel中需要通过list导入,list里面有10万条数据,导入后,可能list就不管了,如果想让jvm跑的更快的话,可以在最后写个list= null。jvm在做垃圾回收计算的时候,就不需要做任何分析直接就回收掉了。

PC:通过上边的代码应该可以明白,jvm在做回收统计的时候真得会一个一个统计的。开发时,借鉴线程安全,接触到大数据的地方,就有泄露的可能,被反被执行,也有可能出现泄露。

0 人点赞