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

相关文章
|
SQL 存储 数据可视化
使用Java分析器优化代码性能,解决OOM问题
使用Java分析器优化代码性能,解决OOM问题
|
4月前
|
缓存 JavaScript Java
常见java OOM异常分析排查思路分析
Java虚拟机(JVM)遇到内存不足时会抛出OutOfMemoryError(OOM)异常。常见OOM情况包括:1) **Java堆空间不足**:大量对象未被及时回收或内存泄漏;2) **线程栈空间不足**:递归过深或大量线程创建;3) **方法区溢出**:类信息过多,如CGLib代理类生成过多;4) **本机内存不足**:JNI调用消耗大量内存;5) **GC造成的内存不足**:频繁GC但效果不佳。解决方法包括调整JVM参数(如-Xmx、-Xss)、优化代码及使用高效垃圾回收器。
192 15
常见java OOM异常分析排查思路分析
|
4月前
|
缓存 JavaScript Java
常见java OOM异常分析排查思路分析
Java虚拟机(JVM)遇到 OutOfMemoryError(OOM)表示内存资源不足。常见OOM情况包括:1) **Java堆空间不足**:内存被大量对象占用且未及时回收,或内存泄漏;解决方法包括调整JVM堆内存大小、优化代码及修复内存泄漏。2) **线程栈空间不足**:单线程栈帧过大或频繁创建线程;可通过优化代码或调整-Xss参数解决。3) **方法区溢出**:运行时生成大量类导致方法区满载;需调整元空间大小或优化类加载机制。4) **本机内存不足**:JNI调用或内存泄漏引起;需检查并优化本机代码。5) **GC造成的内存不足**:频繁GC但效果不佳;需优化JVM参数、代码及垃圾回收器
108 6
常见java OOM异常分析排查思路分析
|
8月前
|
监控 网络协议 NoSQL
java线上排查OOM内存溢出
java线上排查OOM内存溢出
278 1
|
8月前
|
缓存 监控 Java
Java的OOM问题及解决方案
Java的OOM问题及解决方案
164 0
|
8月前
|
缓存 监控 架构师
阿里面试:Java开发中,应如何避免OOM
在Java开发中,OutOfMemoryError(OOM)错误一直是令开发者头疼的问题,也是Java面试中出现核心频率很高的问题。 那么我们究竟怎么样才能够有效正确的管理内存,日常开发中究竟要注意哪些核心技巧来避免OOM错误。 本文将带大家一起学习10个避免OOM的实用小技巧,让大家在工作中能够有的放矢,避免OOM错误的飞来横祸。
125 1
|
8月前
|
监控 Java Linux
java配置OOM时保存堆转储文件
java配置OOM时保存堆转储文件
304 0
|
8月前
|
缓存 架构师 算法
Java内存溢出如何解决,Java oom排查方法,解决办法
在Java开发过程中,有效的内存管理是保证应用程序稳定性和性能的关键。不正确的内存使用可能导致内存泄露甚至是致命的OutOfMemoryError(OOM)。
100 0
|
8月前
|
缓存 架构师 算法
Java内存溢出如何解决,Java oom排查方法,10个定位解决办法
在Java开发过程中,有效的内存管理是保证应用程序稳定性和性能的关键。不正确的内存使用可能导致内存泄露甚至是致命的OutOfMemoryError(OOM)。
150 0
|
IDE Java 开发工具
JAVA的死循环机制
在编程中,循环结构是非常常见和重要的一种语法结构,它可以使程序重复执行某段代码块,直到满足终止条件才停止。然而,如果循环条件永远不满足或者没有正确设置终止条件,就会导致程序陷入死循环。
309 0