深入理解Java虚拟机——Java内存区域与内存溢出异常

版权声明:本人原创,转载需说明文章出处     https://blog.csdn.net/persistencegoing/article/details/88642699

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域:方法区、堆、虚拟机栈、本地方法栈、程序计数器,其中方法区和堆是所有线程共享的数据区,虚拟机栈、本地方法栈和程序计数器是线程私有的内存区域。

程序计数器:是一块较小的内存空间,他可以看作是当前线程所执行的字节码的行号指示器,Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,这块内存区域线程私有。

Java虚拟机栈:线程私有,生命周期与线程相同,虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的时候会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等,每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表存放各种基本类型(boolean,byte,char,short,int,float,long,double)、对象引用(referrence)类型,其中64为的long和double类型的数据会占用2个局部变量空间,其余只占1个,局部变量表所需的内存空间在编译器完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间时完全确定的,如果线程请求的栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈:与Java虚拟机栈类似,线程私有,区别时虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

Java堆:被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例,是垃圾收集器管理的主要区域,也被称为“GC堆”。

方法区:线程共享,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

运行时常量池:是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

图片来自:https://blog.csdn.net/wangcheng_/article/details/80086294
对象的访问:


在IDEA中设置虚拟机参数:

虚拟机参数:-Xms1m -Xmx2m -XX:-HeapDumpOnOutOfMemoryError
代码:

public class Test2 {
 
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        while (true) {
            list.add(new People());
        }
    }
 
}
结果:Java 堆内存溢出

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)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    at java.util.ArrayList.add(ArrayList.java:458)
    at Test2.main(Test2.java:9)
虚拟机参数:-Xss1m
代码:

public class Test3 {
 
    private int length = 0;
 
    private void test(){
        length++;
        test();
    }
 
    public static void main(String[] args) {
        Test3 test3 = new Test3();
        try {
            test3.test();
        }catch (Throwable e){
            System.out.println(test3.length);
            throw e;
        }
 
    }
}
结果:Java虚拟机栈内存溢出

20108
Exception in thread "main" java.lang.StackOverflowError
    at Test3.test(Test3.java:10)
    at Test3.test(Test3.java:10)
    at Test3.test(Test3.java:10)
栈容量只由-Xss参数设定,无论是由于栈帧太大还是虚拟机栈容量太小,当无法分配的时候,虚拟机抛出的都是StackOverflowError异常,操作系统分配给每个进程的内存是有限的,比如32位的Windows限制为2GB,每个线程分配到的栈容量越大,可以建立的线程数据自然减少,建立线程时就越容易吧剩下的内存耗尽,可以通过减少最大堆(Xmx)和减少栈容量(Xss)来换取更多的线程。

博客内容整理来自《深入理解Java虚拟机 JVM高级特性与最佳实践》
 

猜你喜欢

转载自blog.csdn.net/persistencegoing/article/details/88642699
今日推荐