Java基础之JVM内存模型
JVM内存模型
JVM内存模型的标准是拥有五个区域,分别为程序计数器、Java栈、本地方法栈、方法区以及堆;其中程序计数器、Java栈和本地方法栈是线程私有的,方法区和堆是线程共享的;接下来详细介绍下这几个区域。
1. 程序计数器
1.1 程序计数器的介绍
程序计数器只拥有一块很小的内存空间,是唯一一个不会发生内存溢出的区域。主要用来记录当前线程所执行指令的行数,但需要注意的是,如果执行的是本地方法,那么程序计数器是空的。
1.2 程序计数器的作用
- 流程控制:实现程序的顺序执行、选择、循环以及异常处理。
- 线程的上下文切换:帮助线程完成上下文切换工作,快速恢复上次的工作场景。
2. Java栈
2.1 Java栈的介绍
Java虚拟机栈会为每一个即将运行的Java方法创建一块叫做“栈帧”的区域,这块区域用于存储该方法在运行过程中所需要局部变量表、动态链接、操作数栈以及方法出口等信息。
2.2 Java栈的特点
局部变量表的创建是在方法被执行的时候,随着栈帧的创建而创建。而且,局部变量表的大小在编译时期就确定下来了,在创建的时候只需分配事先规定好的大小即可。此外,在方法运行的过程中局部变量表的大小是不会发生改变的。
Java虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。
Java 虚拟机栈会出现两种异常:StackOverFlowError 和 OutOfMemoryError。StackOverFlowError 是栈的请求深度超过当前Java虚拟机栈的最大深度的时候抛出的异常,例如:死递归;OutOfMemoryError是栈请求的内存无法分配时抛出的异常。
3. 本地方法栈
3.1 本地方法栈的介绍
本地方法栈和Java虚拟机栈实现的功能类似,只不过本地方法栈是为本地(native)方法服务的。
4. 方法区
4.1 方法区的介绍
Java 虚拟机规范中定义方法区是堆的一个逻辑部分。方法区中存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等,其中常量存放在运行时常量池之中。
4.2 方法区的“前世今生”
永久代(“PermGen space”)是HotSpot虚拟机对“方法区”的具体实现,这是HotSpot虚拟机特有的,在JRockit(Oracle)、J9(IBM) 并没有“PermGen space”。
在Java7的时候,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap,其中运行时常量池移动到了Java Heap,符号引用(Symbols)转移到了Native Heap。
在Java8的时候,HotSpot将永久代彻底移除了,将剩余未转移的数据都放在了元空间(Metaspace)中。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小。
5. 堆区
堆区的介绍
堆是用来存放对象的内存空间,堆内存随着JVM启动而创建。堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。
若进一步细分,可以将堆区分为年轻代和老年代。其中年轻代中又可以细分为Eden、From Survior以及To Survior三个区域,三者的默认占比为8:1:1,两个Survior区主要是为了方便“***回收”算法处理回收不再使用的对象。