40-对象太多了!堆内存实在是放不下,只能内存溢出!

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 之前通过三篇文章的分析,介绍了 直接内存、Metaspace和栈内存三块区域的内存溢出,同时给出了一些常见的引发内存溢出的场景以及对应解决方案,一般只要vm参数配置合理,代码上不出现大问题,一般不太容易引发对应的OOM

之前通过三篇文章的分析,介绍了 直接内存、Metaspace和栈内存三块区域的内存溢出,同时给出了一些常见的引发内存溢出的场景以及对应解决方案,一般只要vm参数配置合理,代码上不出现大问题,一般不太容易引发对应的OOM。再次通过下图进行回顾:

而本篇文章介绍的堆内存OOM,就是我们的重点了,这块区域才是真正最容易引发内存溢出的。

引发的起因

我们在上图中其实可以发现,每次大量调用方法的时候,方法中的代码都会有创建对象的操作,那么会导致大量的对象进入到新生代,如果新生代放不下,触发Minor GC,则会转移存活对象进入 Survior区域,如果遇到高并发场景下,导致Minor GC过后依然有很多请求未处理完毕,存活对象太多,导致Survior区域放不下,直接进入老年代,如下图所示:

OOM-加载过程图解

一旦当老年代也满了或达到阈值,就会触发Full GC,如果此时老年代GC后发现依然剩下很多存活对象,而新生代GC后需要转移的对象又很多,想放入老年代存放,发现老年代也放不下了,那么此时就会导致OOM,如下图所示

OOM-加载过程图解 (1).png

老年代触发GC后依然无法存放新生代转移过来的对象,没有足够的空间还要继续转移,那么就导致OOM。这就是一种典型的堆内存放不下而导致的内存溢出的一个案例。

堆内存溢出场景总结

一般发生堆内存溢出的场景主要有两种:

  1. 高并发场景,由于请求量非常大,导致大量对象都是存活状态,而大量存活对象放入有限的空间放不下,从而引发OOM,系统崩溃。
  2. 内存泄露场景,系统中存在内存泄露的问题,莫名其妙的产生了大量的对象,并且是存活状态,没有及时取消对象的引用,导致触发GC后还是无法回收,从而引发内存溢出。

因此,总结下就是导致内存泄露的原因:要不就是系统负载过高,要不就是内存泄露问题。

代码模拟堆内存OOM场景

我们通过以下代码来进行模拟:

/**
 * vm参数: -Xms 10m -Xmx 10m
 */
public class OOMTest1 {
   
   
    public static void main(String[] args) {
   
   
        int count = 0;
        List<Object> list = new ArrayList<>();
        while(true){
   
   
            list.add(new Object());
            System.out.println("当前创建了第"+(++count)+"个对象");
        }
    }
}

代码很简单就是通过无限循环,往一个集合里添加对象,而集合是个强引用对象不会被回收,因此当Eden区存满后 ,存活对象均会进入到老年代,直到老年代也装不下后,触发OOM。

打印结果:

image-20210923232157906

在10M的堆内存中,通过最简单的Object对象想要将内存撑满,也需要大概36万个对象。并且控制台里也有明确的提示:OutOfMemoryError : Java heap space 指向堆内存区域。

小结:

ok,通过以上的讲解,我们对堆内存发生OOM的根本原因有了一个理解,以及两种触发堆内存OOM的场景总结,以及通过简单的代码快速模拟了堆内存的OOM溢出。

目录
相关文章
|
存储 安全 算法
深入剖析JVM内存管理与对象创建原理
JVM内存管理,JVM运行时区域,直接内存,对象创建原理。
45 2
|
2月前
|
存储 算法 安全
【JVM】深入理解JVM对象内存分配方式
【JVM】深入理解JVM对象内存分配方式
30 0
|
1月前
|
缓存 Java
Java中循环创建String对象的内存管理分析
Java中循环创建String对象的内存管理分析
25 2
|
3月前
|
弹性计算 运维 搜索推荐
幻兽帕鲁内存溢出怎么办,一键设置定时重启,修改虚拟内存,定时清理,轻松解决卡顿!再也不怕爆内存了!
幻兽帕鲁的内存溢出问题,玩久了确实会变卡。这里给出三个解决思路:第一种方法是定时进行内存清理(装个软件就可以),网上也有很多教程,我会把下载地址放在文章后面,大家可以去下载。第二种方法是调大虚拟内存,这个可以一键设置。第三种方法是定时重启游戏服务,这个也可以一键设置。这三种方法我下面都会教给大家,可以有效解决内存增长过快的问题,避免游戏卡顿甚至崩溃。
486 3
|
3天前
|
存储 Arthas 监控
JVM工作原理与实战(三十):堆内存状况的对比分析
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了堆内存状况的对比分析、产生内存溢出的原因等内容。
10 0
|
3天前
|
监控 Java 测试技术
JVM工作原理与实战(二十八):内存溢出和内存泄漏
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了内存溢出与内存泄漏、内存泄漏的常见场景、解决内存溢出的步骤等内容。
JVM工作原理与实战(二十八):内存溢出和内存泄漏
|
8天前
3.默认值不一样【重点】 局部变量:没有默认值,如果要想使用,必须手动进行赋值 成员变量:如果没有赋值,会有默认值,规则和数组一样 4.内存的位置不一样(了解) 局部变量:位于栈内存 成员变量:位于堆内存 5生命周期不一样(了解)
3.默认值不一样【重点】 局部变量:没有默认值,如果要想使用,必须手动进行赋值 成员变量:如果没有赋值,会有默认值,规则和数组一样 4.内存的位置不一样(了解) 局部变量:位于栈内存 成员变量:位于堆内存 5生命周期不一样(了解)
15 0
|
10天前
为对象分配内存TLAB
为对象分配内存TLAB
|
10天前
|
Java
SpringBoot 项目启动初始化一个Map对象到内存
SpringBoot 项目启动初始化一个Map对象到内存
|
16天前
|
运维 Kubernetes 算法
Java堆内存又溢出了!教你一招必杀技
Java堆内存又溢出了!教你一招必杀技