JVM内存区域与OOM

2018-01-09  本文已影响36人  大大大大大先生

说明:本篇博客属于读书笔记,大量参考《深入理解Java虚拟机》这本书

JVM的内存

程序计数器

Java虚拟机栈

public class DemoMain {

    public static void main(String[] args) {
        System.out.println("test");
        DemoMain.testMethod();
        System.out.println("end");
    }

    public static void testMethod() {
        testMethod();
    }
}

如上的应用程序运行之后,在我的机器上会抛出StackOverflowError:

test
Exception in thread "main" java.lang.StackOverflowError
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)
    at com.lhd.jvmdemo1.DemoMain.testMethod(DemoMain.java:12)

当虚拟机在执行方法testMethod的时候,这时候就会在Java虚拟机栈上创建一个栈帧,然后入栈,然而在testMethod方法内又不断的递归调用testMethod方法,导致Java虚拟机栈不断的嵌套执行testMethod方法,不断的创建testMethod的栈帧,然后入栈,而testMethod并没有执行完成,所以testMethod对应的栈帧不会出栈,当Java虚拟机栈中的栈深度超过了虚拟机允许的深度,这时候就抛出了StackOverflowError异常了,如果虚拟机可以动态拓展,在新的栈帧入栈的时候再去申请内存,要是申请不到足够的内存,此时就会抛出OOM异常了

本地方法栈

Java堆

Object obj = new Object();

obj是对象的引用,存储在Java虚拟机栈中,而new出来的Object对象实例就存储在Java堆中,obj引用指向Java堆中实例的地址,Java堆是垃圾回收管理的主要区域,Java堆的内存空间不需要物理上的连续,只需要逻辑上的连续即可,Java堆也会抛出StackOverflowError 和OOM异常

方法区

运行时常量池

字符串常量池

public class DemoMain {

    public static void main(String[] args) {
        System.out.println("test");
        String s1 = "s1";
        String s2 = "s1";
        String s3 = new String("s1");
        System.out.println(s1 == s2);
        System.out.println("end");
    }
}

以上运行的结果是:

test
true
end

也就是说是s1指向的地址和s2指向的地址是一样的,为什么?因为“s1”这个字符串常量被存储到了字符串常量池中了,虚拟机发现了s1对象指向“s1”,s2对象也指向“s1”,因此不会再次创建一个“s1”,而是将s1和s2对象都指向存储于常量址的“s1”,这里就做到了常量池的对象共享,节省内存

对象创建的过程

对象的访问定位

内存溢出

Java堆内存溢出

public static void main(String[] args) {
        List list = new ArrayList();
        while (true) {
            list.add(new Object());
        }
    }

不断的分配对象,并添加到list当中,这样对象就不会被回收,程序跑一会儿就报Java堆的OOM异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

什么情况可能会导致Java堆的内存泄漏?很明显,内存泄漏,一些对象创建后,一直被持有导致GCRoot一直存在,所以不会被回收

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

public class DemoMain {

    int i = 0;

    public static void main(String[] args) {
        DemoMain demoMain = new DemoMain();
        try {
            demoMain.test();
        } catch (Throwable e) {
            System.err.println("stack:" + demoMain.i);
            e.printStackTrace();
        }
    }

    public void test() {
        i++;
        test();
    }

}

运行结果:

stack:34879
java.lang.StackOverflowError
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
    at com.lhd.jvmdemo1.DemoMain.test(DemoMain.java:22)
public class DemoMain {

    public static void main(String[] args) {
        DemoMain demoMain = new DemoMain();
        demoMain.createThread();
    }

    public void createThread() {
        while (true) {
            new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }

}
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at com.lhd.jvmdemo1.DemoMain.createThread(DemoMain.java:26)
    at com.lhd.jvmdemo1.DemoMain.main(DemoMain.java:12)

这个是本地方法区抛出的OOM异常

方法区和常量池溢出

java.lang.OutOfMemoryError:PermGen space

在android开发中,如果一个apk的类非常多,安装这个apk的时候就可能出现方法区的内存不够用导致方法区内存溢出

上一篇 下一篇

猜你喜欢

热点阅读