内存溢出分析:OutOfMemoryError异常

下面给出个内存区域内存溢出的简单测试方法:

这里写图片描述

一、Java堆溢出

将堆大小限制为20MB,不可扩展(将堆堆最小值参数与最大值参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDumpOnOutOfMemoryError可让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。

import java.util.ArrayList;
import java.util.List;

/**
 * Created by mook on 2017/6/12.
 * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOOM {
    static class OOMObject {}

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();

        while (true) {
            list.add(new OOMObject());
        }
    }
}

另外,由于Java堆内也可能发生内存泄露(Memory Leak),这里简要说明一下内存泄露和内存溢出的区别:

内存泄露是指分配出去的内存没有被回收回来,由于失去了对该内存区域的控制,因而造成了资源的浪费。Java中一般不会产生内存泄露,因为有垃圾回收器自动回收垃圾,但这也不绝对,当我们new了对象,并保存了其引用,但是后面一直没用它,而垃圾回收器又不会去回收它,这边会造成内存泄露,

内存溢出是指程序所需要的内存超出了系统所能分配的内存(包括动态扩展)的上限。


二、虚拟机栈和本地方法栈溢出

栈容量由-Xss参数设定。

  1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
  2. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
/**
 * Created by mook on 2017/6/12.
 * -Xss160k
 */
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}

以上基于单线程的程序将抛出StackOverflowError异常。

如果通过不断建立线程的方式就可以产生内存溢出异常,但是这样产生的异常与栈空间是否足够大并不存在任何联系,在这种情况下,为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。
原因:
操作系统分配给每个进程的内存是有限制的。虚拟机提供参数来控制堆和方法区的内存最大值(Xmx与MaxPermSize),程序计数器消耗的内存很小,可忽略。如果虚拟机进程本身耗费的内存不计算在内,那么剩下的内存就由虚拟机栈和本地方法栈所有了。每个线程分配到的栈容量越大,可建立的线程数就越少,建立线程时就越容易把剩下的内存耗尽。
用例

如果在建立过多线程导致了内存溢出,在不能减少线程数或更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。


三、方法区和运行时常量池溢出

JDK1.7开始逐步“去永久代”

/**
 * Created by mook on 2017/6/12.
 *
 * Returns a canonical representation for the string object.
 * <p>
 * A pool of strings, initially empty, is maintained privately by the
 * class {@code String}.
 * <p>
 * When the intern method is invoked, if the pool already contains a
 * string equal to this {@code String} object as determined by
 * the {@link #equals(Object)} method, then the string from the pool is
 * returned. Otherwise, this {@code String} object is added to the
 * pool and a reference to this {@code String} object is returned.
 * <p>
 * It follows that for any two strings {@code s} and {@code t},
 * {@code s.intern() == t.intern()} is {@code true}
 * if and only if {@code s.equals(t)} is {@code true}.
 * <p>
 * All literal strings and string-valued constant expressions are
 * interned. String literals are defined in section 3.10.5 of the
 * <cite>The Java&trade; Language Specification</cite>.
 *
 * @return  a string that has the same contents as this string, but is
 *          guaranteed to be from a pool of unique strings.
 */
//public native String intern();
public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        //intern()不会复制实例,只是在常量池中记录首次出现的实例引用。
        String str1 = new StringBuilder("计算机").append("软件").toString(); //不会在常量池中存储字符串
        System.out.println(str1.intern() == str1); //true

        String str2 = new StringBuilder("ja").append("va").toString(); //不会在常量池中存储字符串
        System.out.println(str2.intern() == str2); //false,因为常量池中本来就有"java"的引用

        String str3 = "mookmao"; //直接存放在常量池中
        String str4 = new String("mookmao"); //在堆内存中存放
        System.out.println(str3.intern() == str3); //true
        System.out.println(str3.intern() == str4); //false
        System.out.println(str4.intern() == str4); //false
        System.out.println(str3.intern() == str4.intern()); //true
        System.out.println(str3 == str4); //false

        String str5 = new String("kidcao"); //"kidcao"存放在常量池中,new String("kidcao")也会在堆内存中存放
        System.out.println(str5.intern() == str5); //false
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值