GC-案例

问题现象

  • 订单服务器链接不上,超时。
  • 没有熔断措施,导致其他上游服务超时。
  • 服务器雪崩,整个环境不可用

项目目标

  1. 解决线上问题,排查问题,找到具体代码
  2. 分析 JVM

关键事项

  • 使用 Visualvm 分析
  • 下载 dump 文件,分析 dump 文件
  • 基础环境:Java1.8 版本,IDEA,

复现问题

问题的主要就是对象在频繁的申请,频繁的放入,导致堆被撑爆

  1. 编写对应的代码,其实对应的就是频繁的放入 list,让 list 去无限的扩容,这时候如果是面试,就可以问 list 的扩容机制和 map 的扩容机制问题
  package com.example.testjvm;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;

/**
 * 测试堆内存爆表
 *
 * @author admin
 * @date 2023/03/07
 */
@RestController
public class TestController {

    /**
     * 集合对象
     */
    public List<TestJAVA> list = new ArrayList<>();

    /**
     * 会报错的接口
     */
    @GetMapping("/error")
    public void error() {

        for (int i = 0; i < 900000000; i++) {
            TestJAVA build = TestJAVA.builder()
                    .e(i)
                    .f(i)
                    .b(i)
                    .g(i)
                    .a(i).build();
            list.add(build);
        }

    }

    /**
     * 正常的接口
     *
     * @return {@link String}
     * @throws IOException IOException
     */
    @GetMapping("/normal")
    public String normal() {
        return "normal";
    }
}
package com.example.testjvm;

import lombok.Builder;

/**
 * 测试实体类
 *
 * @author admin
 * @date 2023/03/07
 */
@Builder
public class TestJAVA {
    Integer a=0;
    Integer b=0;
    Integer c=0;
    Integer d=0;
    Integer e=0;
    Integer f=0;
    Integer g=0;

    public TestJAVA(Integer a, Integer b, Integer c, Integer d, Integer e, Integer f, Integer g) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
        this.f = f;
        this.g = g;
    }
}
  1. 开始模拟线上请求,在 IDEA 里面设置好对应的参数
# Xmx设置堆内存的最大 Heap 值
# Xms设置堆内存的最小 Heap 值
# +HeapDumpOnOutOfMemoryError可以让JVM在出现内存溢出时候Dump出当前的内存转储快照。快照格式为java_pid2821.hprof(2821为Java进程号)

-Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError

记得设置好对应的 workdirectory 的工作目录

  1. IDEA 配置 VisualVM,安装对应的插件并配置

软件提供如下:
visualvm_215.zip

  1. 用 visualVM 启动


  1. 堆内存爆表

  1. 查看 GC 的命令
sudo -u tomcat jps
sudo -u tomcat jstat -gcutil 3168 1000  #每隔10秒显示堆比例
sudo -u tomcat jstat -gc 3168 10000  #每隔10秒显示堆使用大小,单位KB
jmap -dump:format=b,file=filename pid #线上直接在已经假死的程序上获得dump文件的命令

在刚刚设置的 work 工作目录中有已经爆表的 dump 文件了


数据直接给出了答案,这个对象,很大,这里出了问题

分析问题

  • Eden 区,一直在创建新的对象,一直在请求空间
  • 当 Eden 区达到百分百的时候,触发一次 YGC,然后进行垃圾回收,S0 和 S1 互换,存活的对象,会移动到老年 O 区
  • 当老年 O 区达到百分百的时候,触发一次 FULL GC,进行所有的垃圾回收
  • 所以主要原因是 Eden 区一直在产生对象,撑爆了设定的堆内存

JVM 一些参数说明

  • S0C:第一个幸存区的大小,单位 kb
  • S1C:第二个幸存区的大小,单位 kb
  • S0U:第一个幸存区的使用大小,单位 kb
  • S1U:第二个幸存区的使用大小,单位 kb
  • EC:伊甸园区的大小,单位 kb
  • EU:伊甸园区的使用大小,单位 kb
  • OC:老年代大小,单位 kb
  • OU:老年代使用大小,单位 kb
  • MC:方法区大小,单位 kb
  • MU:方法区使用大小,单位 kb
  • CCSC:压缩类空间大小,单位 kb
  • CCSU:压缩类空间使用大小,单位 kb
  • YGC:年轻代垃圾回收次数,单位 s
  • YGCT:年轻代垃圾回收消耗时间,单位 s
  • FGC:老年代垃圾回收次数,单位 s
  • FGCT:老年代垃圾回收消耗时间,单位 s
  • GCT:垃圾回收消耗总时间,单位 s

内存溢出 out of memory

是指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory;比如申请了一个 integer,但给它存了 long 才能存下的数,那就是内存溢出。
个盘子用尽各种方法只能装 4 个果子,你装了 5 个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.

内存泄露 memory leak

是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak 会最终会导致 out of memory!

常见的内存泄露

  1. 文件流不关闭
  2. 数据库连接没有关闭
  3. 内存使用过多

内存溢出的原因以及解决方法

原因列表:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的 BUG;
  5. 启动参数内存值设定的过小

方案列表:

  1. 第一,修改 JVM 启动参数,直接增加内存。(-Xms,-Xmx 参数一定不要忘记加。)
  2. 第二,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
  3. 第三,对代码进行走查和分析,找出可能发生内存溢出的位置。

重点排查:

  1. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
  2. 检查代码中是否有死循环或递归调用。
  3. 检查是否有大循环重复产生新对象实体。
  4. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
  5. 检查 List、MAP 等集合对象是否有使用完后,未清除的问题。List、MAP 等集合对象会始终存有对对象的引用,使得这些对象不能被 GC 回收。
  6. 使用内存查看工具动态查看内存使用情况

GC-案例
http://example.com/2023/03/07/ri0y3cgry9p5u9et/
作者
杨靖成
发布于
2023年3月7日
许可协议