java死循环为什么不会OOM

简介: java死循环中无限生成对象会不会OOM?

疑问:java死循环中无限生成对象会不会OOM?



答案:有可能会有可能不会。


1.先说会的场景,虚拟机配置:-Xmx10M -Xms10M



代码如下:


public class TestHeapOom {
    static class OOMObject{
    }
    public static void main(String[] args){
        List<OOMObject> list = new ArrayList<OOMObject>();
        while (true){
            list.add(new OOMObject());
        }
    }
}


运行结果如下:


F:\java\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:64209,suspend=y,server=n -Xmx10M -Xms10M ......
Connected to the target VM, address: '127.0.0.1:64209', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:64209', transport: 'socket'
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
      at java.util.Arrays.copyOf(Arrays.java:3210)
      at java.util.Arrays.copyOf(Arrays.java:3181)
      at java.util.ArrayList.grow(ArrayList.java:261)
      at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
      at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
      at java.util.ArrayList.add(ArrayList.java:458)
      at com.example.demo.OOM.TestHeapOom.main(TestHeapOom.java:15)
    Process finished with exit code 1


为什么会OOM?


一直new对象,并将new出的对象指向了list,占用内存在某一时刻大于10M了,就OOM了,那么多问自己一句为什么这么多对象没有被回收?因为可达性分析算法判定该对象仍是可达状态不可回收,jvm中在不断的new对象,这些对象没有被回收有一个前提,那就是仍是可达状态,为什么可达,因为list相当于这些new出的对象的GC Roots(总共4种对象可作为GC Roots,最典型的就是栈中引用指向的对象),可达性分析算法规定,对象与GC Roots之间存在引用链,则不回收,所以会OOM.


2.再说不会的场景,虚拟机配置:-Xmx10M -Xms10M



代码如下:


public class TestHeapOom {
    static class OOMObject{
    }
  public static void main(String[] args){
        while (true){
            List<OOMObject> list = new ArrayList<OOMObject>();
            list.add(new OOMObject());
        }
    }


使用 jvisualvm.exe 工具分析堆内存占用情况如下:


20201208194013808.png


可以发现内存占用到达一定高度后就会触发GC回收内存,并不会有OOM的情况,两处代码的唯一区别也就是一下这行代码的位置,一个在循环外,一个在循环内。


List<OOMObject> list = new ArrayList<OOMObject>();


为什么不会OOM?


按照可达不可达的算法,这种情况new出的对象也会有引用链存在,为什么会被回收了呢?


猜想一:虽说每个new对象都有对应的list对象作为GC Roots但是每个对象都是相同的,会不会有其他机制判断相同同样会是GC的目标?


验证猜想一代码:


public class TestHeapOom {
    static class OOMObject{
       String name;
       public OOMObject(String name){
           this.name = name;
       }
    }
    public static void main(String[] args){
        int n =0;
        while (true){
            List<OOMObject> list = new ArrayList<OOMObject>();
            list.add(new OOMObject("a"+n));
        }
    }
}


堆内存占用情况如下图:


20201208195705646.png


猜想一结论:很明显内存占用到达一定高度就会触发GC,说明这些对象还是留不住,显然不是因为对象都是相同的就给回收了。


猜想二:对象的回收应该还有其他的判定,作为可达性分析算法的补充,或者我理解的可达性分析算法有误,暂时没有解决方案,记录在此,下次再研究,如有路过的朋友知道答案,还望不吝赐教,感激不尽。


3 不会OOM的真正原因



3.1先对几个概念进行说明


3.1先对几个概念进行说明


①除static修饰的方法外,其余方法的局部变量表(不清楚这是啥的,需要先看看JVM的运行时数据区)最小值是1,这个1是用于存储该方法所属类的实例变量的,这是一个隐式加载每个非static方法都是如此,在方法内使用this调用的就是这个隐式对象。


②局部变量表中的变量槽的个数在编译器就已经确定,且局部变量在方法内部也是有作用范围的,当局部变量超出自身作用范围后,如果需要,这个已经超过使用范围的局部变量所占用的局部变量槽会被其他变量占用,这也就是局部变量槽可以复用的场景。


③局部变量表是最常见的一种GC Roots。


3.2 原因解释


首先看下2里面代码的class文件,可以清晰的看到main方法只拥有3个局部变量槽,一个是 传入参数args占用,一个是局部变量n占用,一个是局部变量list占用,既然只有三个变量槽,那无限生成的list会存储到哪里?看下3.1中的②,因为局部变量槽是可以复用的,后期生成的list其实都是在占用之前的变量槽,即使无限生成对象,其实局部变量表中也就只有一个对象指向到堆中,其余堆中new出来的对象是没有引用指向他们的,没有引用指向他们,他们自然就是垃圾了,那么只要有GC触发他们就会被回收。这也就是为什么不会OOM的原因了。


20210107094809543.png

相关文章
|
7月前
|
SQL 存储 数据可视化
使用Java分析器优化代码性能,解决OOM问题
使用Java分析器优化代码性能,解决OOM问题
|
26天前
|
监控 网络协议 NoSQL
java线上排查OOM内存溢出
java线上排查OOM内存溢出
22 0
|
2月前
|
缓存 监控 架构师
阿里面试:Java开发中,应如何避免OOM
在Java开发中,OutOfMemoryError(OOM)错误一直是令开发者头疼的问题,也是Java面试中出现核心频率很高的问题。 那么我们究竟怎么样才能够有效正确的管理内存,日常开发中究竟要注意哪些核心技巧来避免OOM错误。 本文将带大家一起学习10个避免OOM的实用小技巧,让大家在工作中能够有的放矢,避免OOM错误的飞来横祸。
49 1
|
3月前
|
监控 Java Linux
java配置OOM时保存堆转储文件
java配置OOM时保存堆转储文件
53 0
|
3月前
|
缓存 架构师 算法
Java内存溢出如何解决,Java oom排查方法,解决办法
在Java开发过程中,有效的内存管理是保证应用程序稳定性和性能的关键。不正确的内存使用可能导致内存泄露甚至是致命的OutOfMemoryError(OOM)。
|
3月前
|
缓存 架构师 算法
Java内存溢出如何解决,Java oom排查方法,10个定位解决办法
在Java开发过程中,有效的内存管理是保证应用程序稳定性和性能的关键。不正确的内存使用可能导致内存泄露甚至是致命的OutOfMemoryError(OOM)。
|
9月前
|
算法 安全 Java
【Java面试】HashMap死循环问题
【Java面试】HashMap死循环问题
54 0
|
9月前
|
消息中间件 缓存 监控
36-Java程序员的梦魇:OOM内存溢出!
作为一名Java程序员,平时在公司拼命加班、任劳任怨干活,好不容易熬夜开发完系统,测试完毕,Bug修复完毕,然后上线部署系统,系统开始正常运行,终于歇口气能好好放松下,喝杯Java压压惊了,But!上线后最害怕的事是什么?
95 0
|
9月前
|
IDE Java 开发工具
JAVA的死循环机制
在编程中,循环结构是非常常见和重要的一种语法结构,它可以使程序重复执行某段代码块,直到满足终止条件才停止。然而,如果循环条件永远不满足或者没有正确设置终止条件,就会导致程序陷入死循环。
180 0
|
9月前
|
Arthas Dubbo Oracle
Java虚拟机OOM解析
针对以Java主导的企业级应用开发,Java虚拟机是整个项目架构的灵魂所在。只有弄清楚其内存分配及垃圾回收机制才能够在项目建设活动过程中游刃而余,无论是基于当前流行的微服务体系(以Spring家族的 Spring Cloud或以Ali家族的Dubbo)or 即将(已经)流行的服务网格体系。
80 1