Java内存区域与内存溢出异常-上

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/uotail/article/details/83352485

 

 一、根据java虚拟机规范(java se7)的规定,java虚拟机所管理的内存包含以下几个运行时数据区域,如下图所示:

java虚拟机运行时数据区

就这五个区域,很直观了吧

接下来对这几个区域一一介绍

1、程序计数器 

       程序计数器 是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器
在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令,因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为 “线程私有的内存”。
如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native 方法,这个计数器值则为空(Undefined)。
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2、java虚拟机栈

       与程序计数器一样,java虚拟机栈 也是线程私有的,它的生命周期与线程相同。
虚拟机栈描述的是Java方法执行的内存模型:
每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。                                                           

可见对于我们写代码的来说,必须要理解 虚拟机栈中的 局部变量表。

感觉书上的内容句句都是充满智慧的。

但在这里我们又要引出一个概念  栈深度。什么是栈深度?

       我们已经知道在JAVA 虚拟机中,每个方法在执行的同时都会创建一个栈帧,来储存一些方法相关的信息。当方法调用的时候它被压入虚拟机栈,方法结束出栈。                                                                                                                                                    但在递归的方法中: 每一次递归调用它本身的方法都会往虚拟机栈中压入一个栈帧,如果是无限递归,就会一直往虚拟机栈中压栈,这样到某一个时候会栈深度大于虚拟机栈允许的深度。抛出StackOverflowError。栈溢出!

如下面程序所示:

public class Test {
    private int count = 0;
    public void testAdd() {
        count++;
        testAdd();//每一次递归调用它的这个方法都会往虚拟机栈中压入一个栈帧
    }
    public void test() {
        try {
            testAdd();
        } catch (Throwable e) {
            System.out.println(e);
            System.out.println("栈深度:" + count);
        }
    }
    public static void main(String[] args) {
        new Test().test();
    }
}

运行后控制台如下:

栈深度:19687
栈的高度称为栈的深度,栈深度受栈帧大小影响。

通俗的说,栈的深度就是在一个线程中可同时调用多少个方法,每调用一个方法就压入一个栈帧,即可以压入多少个栈帧而不出栈。

通过-Xss可以设置栈的大小

知道了栈深度,该怎么用呢?对JVM调优有什么用呢?

当JVM我们定义的方法参数和局部变量过多,字节过大,考虑到可能会导致栈深度多小,可能使程序出现错误。

这个时候就需要手动的增加栈的深度,避免出错。

而且当看到StackOverFlow的时候我们也可以知道可能是栈溢出造成的错误。知道如何去解决。这才是最重要的。

3、本地方法栈

4、java堆

简单的总结,堆就是存放 对象和数组的,可以通过 -Xms  -Xmx控制

5、方法区

 

我们已经知道 堆内存从垃圾回收的角度可以分为新生代和老年代,方法区则被称为永久代。永久代有 -XX:MaxPermSize的上限。方法区主要存放常量池和类型

5.1、运行时常量池

6、直接内存(了解下)

参考 :深入理解java虚拟机 ···这本书

          https://blog.csdn.net/qq_28385797/article/details/53468665

猜你喜欢

转载自blog.csdn.net/uotail/article/details/83352485