内存溢出的几种情况(Java语言,1.8)

781 阅读1分钟

内存溢出的几种情况(Java语言,1.8)

1、栈内存溢出

/**
 * 演示栈内存溢出 java.lang.StackOverflowError
 * -Xss256k
 */
public class Demo1_2 {
    private static int count;
​
    public static void main(String[] args) {
        try {
            method1();
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(count);
        }
    }
​
    private static void method1() {
        count++;
        method1();
    }
}
​

栈内存溢出效果.gif

method1()方法不停的入栈,直至栈内存被占满而抛出错误

2、堆内存溢出

import java.util.ArrayList;
import java.util.List;
​
/**
 * 演示堆内存溢出 java.lang.OutOfMemoryError: Java heap space
 * -Xmx8m
 */
public class Demo1_5 {
​
    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "hello";
            while (true) {
                // 添加字符串是为了保证该字符串不会被回收
                list.add(a); // hello, hellohello, hellohellohellohello ...
                // 通过反编译可以看出 下面的操作是由:new StringBuilder().append(a).append(a).toString() 组成的
                // 而toString() 方法也是通过new String() ,会被放到堆内存中
                a = a + a;  // hellohellohellohello
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(i);
        }
    }
}

image-20220707091150388.png

堆内存溢出效果.gif

3、元空间内存溢出

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
​
/**
 * 演示元空间内存溢出 java.lang.OutOfMemoryError: Metaspace
 * -XX:MaxMetaspaceSize=15m:设置元空间大小   -XX:-UseCompressedClassPointers:不允许类指针压缩
 * -XX:MaxMetaspaceSize=15m -XX:-UseCompressedClassPointers
 */
public class Demo1_8 extends ClassLoader { // 可以用来加载类的二进制字节码
    public static void main(String[] args) {
        int j = 0;
        try {
            Demo1_8 test = new Demo1_8();
            for (int i = 0; i < 20000; i++, j++) {
                // ClassWriter 作用是生成类的二进制字节码
                ClassWriter cw = new ClassWriter(0);
                // 版本号, public, 类名, 包名, 父类, 接口
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                // 返回 byte[]
                byte[] code = cw.toByteArray();
                // 执行了类的加载
                test.defineClass("Class" + i, code, 0, code.length); // Class 对象
            }
        } finally {
            System.out.println(j);
        }
    }
}
​

没有设置 -XX:-UseCompressedClassPointers 参数的输出结果

image-20220707085246257.png

image-20220707085310474.png

设置 -XX:-UseCompressedClassPointers 参数的输出结果

image-20220707085212013.png

image-20220707085145525.png

4、直接内存溢出

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
​
​
/**
 * 演示直接内存溢出
 */
public class Demo1_10 {
    static int _100Mb = 1024 * 1024 * 100;
​
    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
        // 方法区是jvm规范, jdk6 中对方法区的实现称为永久代
        //                  jdk8 对方法区的实现称为元空间
    }
}

image-20220707092710340.png