注:本文资料收集自网络,一部分资料来源于大佬NO0b的博客,原博文链接:点我
JDK:Java开发工具 | JRE:Java运行环境 | JVM:Java虚拟机 (对应平台的虚拟机)
JVM(Java Virtual Machine),意为Java虚拟机
Java的内存区域
Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程:
首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。
在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。
因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)
根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:
程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
1.1 程序计数器(线程私有)
程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。
如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的是一个Native方法,这个计数器值为空。
程序计数器内存区域是唯一一个在JVM规范中没有规定任何OOM情况的区域!
什么是线程私有?
由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的。
因此,在任一具体时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令。那么,为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储。我们就把类似这类区域称之为"线程私有"的内存。
因此,可以这么说:程序计数器是每个线程所私有的。
1.2 Java虚拟机栈(线程私有)
Java栈也称作虚拟机栈(Java Vitual Machine Stack)。
虚拟机栈描述的是Java方法执行的内存模型 : 每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。生命周期与线程相同。
当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。
之前我们一直讲的栈区域实际上就是此处的虚拟机栈,再详细一点,是虚拟机栈中的局部变量表部分。
局部变量表 : 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用和returnAddress类型(它指向了一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。
此区域一共会产生以下两种异常:
1. 如果线程请求的栈深度大于虚拟机所允许的深度(-Xss设置栈容量),将会抛出StackOverFlowError异常。
2. 虚拟机在动态扩展时无法申请到足够的内存,会抛出OOM(OutOfMemoryError)异常
1.3 本地方法栈(线程私有)
本地方法栈与虚拟机栈的作用完全一样,他俩的区别无非是本地方法栈为虚拟机使用的Native方法服务,而虚拟机栈为JVM执行的Java方法服务。
在HotSpot虚拟机中,本地方法栈与虚拟机栈是同一块内存区域。
1.4 Java堆(线程共享)
Java中的堆是用来存储对象本身以及数组(当然,数组引用是存放在Java栈中的)。
在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。因此这部分空间也是Java垃圾收集器管理的主要区域。
另外,堆是被所有线程共享的,在JVM中只有一个堆。
1.5 方法区(线程共享)
方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。
在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
JVM规范规定:当方法区无法满足内存分配需求时,将抛出OOM异常。
运行时常量池是方法区的一部分,存放字面量与符号引用。它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。
字面量 : 字符串(JDK1.7后移动到堆中) 、final常量、基本数据类型的值。
符号引用 : 类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。