java死循环会导致内存溢出吗?java内存溢出有哪些情况?

我们知道,java死循环会在很大的一段时间内,都无法获取接口的返回,程序好像进入假死状态一样,这样是不利于程序的运行的,那java死循环会导致内存溢出吗?下面来我们就来给大家讲解一下。

Java死循环不会造成 JVM 内存溢出!

java内存溢出有哪些情况?

JVM提供的内存管理机制和自动垃圾回收极大的解放了用户对于内存的管理,大部分情况下不会出现内存泄漏和内存溢出问题。但是基本不会出现并不等于不会出现,所以掌握Java内存模型原理和学会分析出现的内存溢出或内存泄漏,对于使用Java的用户来说仍然十分重要。

Java中内存溢出常见于如下的几种情形:

栈内存溢出(StackOverflowError);

堆内存溢出(OutOfMemoryError:java heap space);

永久代溢出(OutOfMemoryError:PermGen sapce);

不同的内存溢出错误可能会发生在内存模型的不同区域,因此,我们需要根据出现错误的代码具体分析来找出可能导致错误发生的地方,并想办法进行解决。

2. 栈内存溢出

栈内存可以分为虚拟机栈(VM Stack)和本地方法栈(Native Method Stack),除了它们分别用于执行Java方法(字节码)和本地方法,其余部分原理是类似的(以虚拟机栈为例说明)。Java虚拟机栈是线程私有的,当线程中方法被调度时,虚拟机会创建用于保存局部变量表、操作数栈、动态连接和方法出口等信息的栈帧(Stack Frame)。

具体来说,当线程执行某个方法时,JVM会创建栈帧并压栈,此时刚压栈的栈帧就成为了当前栈帧。如果该方法进行递归调用时,JVM每次都会将保存了当前方法数据的栈帧压栈,每次栈帧中的数据都是对当前方法数据的一份拷贝。如果递归的次数足够多,多到栈中栈帧所使用的内存超出了栈内存的最大容量,此时JVM就会抛出StackOverflowError。

下面我们下一个不断的递归调用自己的方法,然后执行该程序:

public class StackOverflowErrorDemo
{
    private static int stackLength = 0;
    public static void main(String[] args)
    {
        StackOverflowErrorDemo demo = new StackOverflowErrorDemo();
        try
        {
            demo.pusStack();
        }
        catch (Throwable e)
        {
            System.out.println("stack length is: " + demo.stackLength);
            throw e;
        }
    }
    public void pusStack()
    {
        stackLength++;
        pusStack();
    }
}

运行程序很快就会抛出异常,异常信息如下所示。从输出信息中发现,出现问题的地方就是程序中递归调用方法自身的地方。

stack length is: 20315
Exception in thread "main" java.lang.StackOverflowError
at 
OutOfMemoryErrorDemo.StackOverflowErrorDemo.pusStack(StackOverflowErrorDemo.java:16)
at 
OutOfMemoryErrorDemo.StackOverflowErrorDemo.pusStack(StackOverflowErrorDemo.java:16)
at 
OutOfMemoryErrorDemo.StackOverflowErrorDemo.pusStack(StackOverflowErrorDemo.java:16)
......

总之,不论是因为栈帧太大还是栈内存太小,当新的栈帧内存无法被分配时,JVM就会抛出StackOverFlowError。通常栈内存可以通过设置-Xss参数来改变大小。

3. 堆内存溢出

堆内存的唯一作用就是存放数组和对象实例,即通过new指令创建的对象,包括数组和引用类型。堆内存溢出又分为两种情况:

堆内存溢出:当堆中对象实例所占的内存空间超出了堆内存的最大容量,JVM就会抛出OutOfMemoryError:java heap space异常

堆内存泄露:当堆中一些对象不再被引用但垃圾回收器无法识别时,这些未使用的对象就会在堆内存空间中无限期存在,不断的堆积就会造成内存泄漏

如果是因为堆内存空间太小,可以通过改变-Xmx来进行调整,或者分析程序中对象的生命周期和存储结构等信息进行调整;如果发生了内存泄漏,则可以先找出导致泄漏发生的对象是如何被GC ROOT引用起来的,然后通过分析引用链找到发生泄漏的地方。

例如,我们通过-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError来设置堆内存大小为20M,并且设定不支持自动扩展,同时使用-XX:+HeapDumpOnOutOfMemoryError实现当异常抛出时Dump出当前的内存堆转储快照进行分析。

import java.util.ArrayList;
public class HeapOOMDemo
{
    static class OOMObject
    {}
    public static void main(String[] args)
    {
        ArrayListlist = new ArrayList < > ();
        HeapOOMDemo demo = new HeapOOMDemo();
        try
        {
            while (true)
            {
                list.add(new OOMObject());
            }
        }
        catch (Throwable e)
        {
            System.out.println(list.size());
            throw e;
        }
    }
}

运行程序一段时间后输出如下信息:

70091070
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3721)
at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
at java.base/java.util.ArrayList.grow(ArrayList.java:235)
......

Java中内存溢出常见就是栈内存溢出以及堆内存溢出,不同的内存溢出错误可能会发生在内存模型的不同区域,所以我们需要找出发生错误的原因然后做出解决方法!最后大家如果想要了解更多java常见问答知识,敬请关注奇Q工具网。

推荐阅读:

json的基本语法规则是什么?json语法规则简介

jsf框架设计模式有哪些?jsf框架设计模式介绍

学java可以考什么证书?如何做更容易考证?