jvm(五)方法區、堆和虛擬機棧內存溢出

關於方法區、堆和虛擬機棧我們已經知道了它們的職責。
這裏會有個疑惑的地方:
方法區存放的是類的元信息、靜態變量和常量,存滿了怎麼辦?
堆存放的是對象,存滿了怎麼辦?
虛擬機棧會進行一系列方法的壓棧出棧,棧滿了怎麼辦?
其實好辦,這就是所謂的內存溢出了。

構造堆內存溢出

這個其實很好構建,寫個死循環代碼,然後不停的產生對象,把堆撐爆了,不就堆內存溢出了。
代碼

	/**
     * 爲了讓堆內存溢出快速暴露,這裏設置堆內存大小爲:-Xmx32M -Xms32M
     * @return
     */
    @RequestMapping("/heap")
    public String heap(){
        List<User> list=new ArrayList<>();
        int i=0;
        while (true){
            list.add(new User(i++, UUID.randomUUID().toString()));
        }

    }

User對象就是一個普通對象,沒啥東西。

@Data
@AllArgsConstructor
public class User {

    private Integer id;
    private String name;

}

IDEA修改jvm堆內存大小,然後再啓動。
在這裏插入圖片描述
執行heap方法後,內存溢出的報錯降臨了。
在這裏插入圖片描述

構造方法區(Meta Space)溢出

自jdk1.8後,有了一個堆外內存,它是操作系統本地內存,一般用來存放class對象。一樣的道理,寫個死循環代碼,不停的產生class對象,把它撐爆。
代碼

	/**
     * 非堆內存溢出(Meta Space)
     * 爲了讓非堆內存溢出快速暴露,這裏設置大小爲:-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M
     * @return
     */
    @RequestMapping("noheap")
    public String noheap() {
        List<Class<?>> list = new ArrayList<>();
        int i = 0;
        while (true) {
            list.addAll(Metaspace.createClasses());
        }
    }

Metaspace是個工具類,用來產生class對象。

public class Metaspace extends ClassLoader {
	
    public static List<Class<?>> createClasses() {
        // 類持有
        List<Class<?>> classes = new ArrayList<Class<?>>();
        // 循環1000w次生成1000w個不同的類。
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            // 定義一個類名稱爲Class{i},它的訪問域爲public,父類爲java.lang.Object,不實現任何接口
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
                    "java/lang/Object", null);
            // 定義構造函數<init>方法
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                    "()V", null, null);
            // 第一個指令爲加載this
            mw.visitVarInsn(Opcodes.ALOAD, 0);
            // 第二個指令爲調用父類Object的構造函數
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
                    "<init>", "()V");
            // 第三條指令爲return
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();
            Metaspace test = new Metaspace();
            byte[] code = cw.toByteArray();
            // 定義類
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
            classes.add(exampleClass);
        }
        return classes;
    }
}

maven裏還需要引入asm依賴包。

<dependency>
	<groupId>asm</groupId>
	<artifactId>asm</artifactId>
	<version>3.3.1</version>
</dependency>

IDEA修改jvm堆內存大小,然後再啓動。
在這裏插入圖片描述
執行noheap方法後,堆外內存溢出也暴露出來了。
在這裏插入圖片描述

虛擬機棧溢出

弄一個遞歸方法 ,不斷的去壓棧導致棧溢出。
代碼

public class Test06 {

    public static long count = 0;

    public static void method(long i) {
        System.out.println(count++);
        method(i);
    }

    public static void main(String[] args) {
        method(1);
    }
}

執行完後,可見內存溢出。
在這裏插入圖片描述
這裏還可以將棧的深度設置小一點,這樣它的溢出速度就更快了。
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章