运行时数据区域
JVM会在执行Java程序的过程中把他管理的内存划分成若干个不同的数据区域。这些数据区域各有各的用处,各自区域内数据的创建与销毁相互独立,有的区域随着JVM进程启动而存在,有的区域则依赖用户线程的启动和结束而创建与销毁。
一般JVM将所管理的内存取悦分为六块,分别是:
- 程序计算器
- Java虚拟机栈
- 本地方法栈
- Java堆
- 方法区
- 运行时常量池
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令。例如为了为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储。我们就把类似这类区域称之为”线程私有”的内存。
线程私有的区域是程序计数器,Java虚拟机栈和本地方法堆。线程共享的是Java堆,方法区以及运行时常量池。
1. 程序计数器(线程私有)
程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,即当前程序运行的位置。
如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。
如果正在执行的是一个Native方法,这个计数器值为空,这个特性和Java的BootStrap类加载器(Java ClassLoader类加载器,自定义类加载器以及双亲委派模型)对Java程序不可见很相似,因为native方法为本地C/C++方法,而Java虚拟机以及Java程序是看不懂它们,所以导致了这样的结果。
另外,程序计数器是六大区域内唯一没有OOM(OutOfMemoryError)的区域。
2. Java虚拟机栈(线程私有)
虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。其生命周期与对应的线程的生命周期相同。
Java虚拟机栈主要存放的就是线程的局部变量表。局部变量表指的是Java八大基本数据类型(boolean, byte, char, short, int, long, float, double)以及对象的引用。
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在Java虚拟机栈中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。
虚拟机栈会发生下列两种异常:
- StackOverFlowError
如果线程请求的栈深度大于虚拟机栈允许的深度,将会抛出该异常。可以通过编译参数 -Xss 修改编译器栈的容量
- OOM(OutOfMemoryError)
JVM在动态扩展是无法申请的足够的内存,就会抛出该异常。
Java虚拟机栈的创建与回收由JVM自动进行,我们不需要关心。
3. 本地方法栈(线程私有)
本地方法栈其实与Java虚拟机栈作用一样,只不过本地方法栈服务的是native本地方法。其他的与Java虚拟机栈一致。
在最新的HotSpot虚拟机中,本地方法栈和虚拟机栈共享同一片区域。
4. Java堆(线程共享)
Java堆(Java Heap)是JVM所管理的最大内存区域,也是所有线程共享的一块区域,在JVM启动时创建。
此内存区域存放的都是对象的实例和数组。JVM规范中说到:”所有的对象实例以及数组都要在堆上分配”。
Java堆是垃圾回收器管理的主要区域,百分之九十九的垃圾回收发生在Java堆,另外百分之一发生在方法区,因此又称之为”GC堆”。根据JVM规范规定的内容,Java堆可以处于物理上不连续的内存空间中。
通过编译参数可以手动扩展Java堆的大小(-Xmx设置最大值,-Xms设置最小值)。
Java堆存在OOM,当堆中没有足够的内存完成实例化分配并且也无法扩展时,将会抛出此异常。
5. 方法区(线程共享)
方法区也是所有线程共享的一片内存区域。它存储的是已被JVM加载的类信息,常亮,静态变量,编译器编译后的代码等数据。在JDK1.8以前的HotSpot虚拟机中,方法区也被称为永久代,1.8后被元空间取代。
方法区称为永久代并不意味这进入方法区就永久存在,方法区也会发生内存回收,此区域的内存回收主要是针对常量池的回收以及对类型的卸载(后面的文章中会详细介绍)。
当方法区无法满足内存分配需求时,将抛出OOM异常。
6. 运行时常量池(线程共享)
其实运行时常量池是方法区的一部分,用来存放字面量与符号引用。
字面量指的是字符串(JDK1.7之后字符串在Java堆中存放),final常量,基本数据类型的值(基本数据类型的引用在虚拟机栈存放,值在运行时常量池中)。
符号引用指的是类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。
关于内存区域发生的异常会在下一节进行介绍。