JVM的运行时数据区域
JVM会在执行Java程序的过程中把它管理的内存划分为若干个不同的数据区域,这些数据区域各有各的用处,各有各的创建与销毁时间,有的区域随着JVM进程的启动而存在,有的区域则依赖于用户线程的启动和结束而创建与销毁,一般来说:JVM管理的内存将会包含以下几个运行时的数据区域:
线程私有区域:程序计数器、Java虚拟机栈、本地方法栈
线程共享区域:Java堆、方法区、运行时常量池
1.程序计数器
程序计数器是一块比较小的内存空间,可以看作是当前线程锁执行的字节码的行号指示器。
如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器值为空。
程序计数器内存区域是唯一一个在JVM规范中没有规定任何 Out Of Memory 情况的区域。
这个我们也十分容易理解,我们的程序计数器只是存储正在执行的虚拟机字节码的指令的地址,是一个定长的数据,所以它不会存在内存溢出的情况。所以我们不需要为他规定任何OOM情况。
因为每一个线程都需要自己的程序计数器,去记录执行到的行数,以便当发生线程从运行到阻塞再回到运行的时候,能从正确的行数开始执行 。
2.Java虚拟机栈
虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法调用至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程,声明周期与线程相同。
我们在Java中学习的栈区实际上就是此处我们的虚拟机栈,详细一点就是我们的虚拟机栈中的局部变量表的部分。
局部变量表
这个地方可以存放8大基本数据类型、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法的时候, 这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表的大小。
在这个区域会抛出两种异常:栈溢出和OOM异常
3.本地方法栈
和前面的虚拟机栈相同,只是本地方法栈是为了Native方法服务, 在Hotspot版本的JVM中本地方法栈与虚拟机栈合二为一
4.Java的堆
Java堆(Java Heap)是JVM所管理的最大内存区域,Java堆是所有线程共享的一块区域,在JVM启动时创建,此内存区域存放都是对象实例。
“所有对象实例以及数组都要在堆上分配” Java堆是垃圾回收器管理的最大内存区域。因此很多时候可以称之为“GC堆”。根据JVM规范规定的内容,Java堆可以处于物理上不连续的内存空间中。当对无法扩展且其中的空间不够时会抛出OOM异常。
5.方法区
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。想要完成反射就得依靠这一块区域的信息。
运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。