Java OOM(2-3)

摘录自《深入理解Java虚拟机》 第二版 周志明

  • 以下内容都是我对书籍内容的个人理解,可能存在错误,若要深入学习,建议翻看原书籍

除了程序计数器,其他区域都规定了OutOfMemoryError,现在逐个去尝试使其出错

① Java堆

思路:堆是用来存放对象的,所以只要一直创建对象即可

第一次尝试:

结果:失败,未出现堆溢出
解释:执行死循环创建对象,但是这些对象都没有没引用,是可以被GC的对象,当内存不足时,作为非强引用对象,都会被回收

public static void main(String[] args) throws Exception {
    while (true){
        new Integer(0);
    }
}
第二次尝试:

结果:成功,堆溢出
解释:将创建的对象都纳入数组,完成引用,阻止GC

public static void main(String[] args) throws Exception {
    List<Integer> list=new ArrayList<>();
    while (true){
        list.add(new Integer(0));
    }
}
错误日志
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid12444.hprof ...
Heap dump file created [32471772 bytes in 0.190 secs]
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)
	...

② Java栈与本地方法栈

思路:
a)在Sun HotSpot虚拟机中,Java栈和本地方法栈是合并的,所以一起测试
b)往栈中一直压入栈帧,即方法递归调用,然后打印栈深度

第一次尝试:

结果:失败,catch语句执行失败
解释:Throwable类有两个子类,一个是Exception异常,一个是Error错误,而堆溢出属于错误Error

public static void main(String[] args) throws Exception {
    int stackLength = 0;
    try {
        method(stackLength);
    } catch (Exception e) {
        System.out.println(stackLength);
        throw e;
    }
}

public static void method(int stackLength) {
    stackLength++;
    method(stackLength);
}
第二次尝试:

结果:失败,打印出的栈深度为0
解释:基本数据类型是值传递

public static void main(String[] args) throws Exception {
    int stackLength = 0;
    try {
        method(stackLength);
    } catch (Throwable e) {
        System.out.println(stackLength);
        throw e;
    }
}

public static void method(int stackLength) {
    stackLength++;
    method(stackLength);
}
第三次尝试:

结果:失败,打印出的栈深度为0
解释:包装类Integer在执行完+1动作后,实际上是创建了一个新对象,而不是在原对象上进行修改,而每次递归后创建了新栈帧,在每个新栈帧中都拥有自己的局部变量表,+1后的对象都被这个新栈帧局部变量表的新引用所引用,最初的栈帧main方法的stackLength引用的还是最初的对象,也就是0

public static void main(String[] args) throws Exception {
    Integer stackLength = 0;
    try {
        method(stackLength);
    } catch (Throwable e) {
        System.out.println(stackLength);
        throw e;
    }
}

public static void method(Integer stackLength) {
    stackLength++;
    method(stackLength);
}
第四次尝试:

结果:成功,栈溢出
解释:方法递归,没有出口,栈帧数量超过栈的最大深度且栈无法再扩展

private static Integer stackLength;

public static void main(String[] args) throws Exception {
    stackLength = 0;
    try {
        method();
    } catch (Throwable e) {
        System.out.println(stackLength);
        throw e;
    }
}

public static void method() {
    stackLength++;
    method();
}

错误日志:

10939 
Exception in thread "main" java.lang.StackOverflowError
	at com.kingdon.Demo.method(Demo.java:25)
	at com.kingdon.Demo.method(Demo.java:26)
	...
	
此处用-Xss128k设置完栈最大内存后,栈最大深度从10939变成了900

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

思路:
a)方法区存放的是一些运行时的常量,以及类的信息,此处考虑创建大量常量
b)此时想到的就是String类的intern()方法,JDK7以后,intern方法,首先检查运行时常量池是否有该字符串的引用,如果有,直接返回该引用,如果没有,新建一个引用指向该字符串对象,并返回该引用

Java8运行时,提示
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0

网上一查,永久代被移除了,引入新概念,元空间,而且运行出来的异常是

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

你可能感兴趣的:(JVM)