一、JVM 运行时数据区域
划分为 5 部分:
- 程序计数器(PC)
- JAVA(虚拟机)栈
- 本地方法栈
- 方法区
- 堆
==================================================================================================
1、程序计数器 线程私有
组原、汇编里都有,这里直接放定义:
程序计数器(Program Counter Register),也有称作为PC寄存器。程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。
虽然JVM中的程序计数器没有物理概念上的CPU寄存器,但是JVM中的程序计数器的功能跟汇编语言中的程序计数器的功能在逻辑上是等同的,也就是说是用来指示 执行哪条指令的。
- 多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令
- 为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,互不影响。
- 如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined
- 由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory) OOM
2、JAVA(虚拟机)栈 线程私有
虚拟机栈描述的是Java方法执行的内存模型。(严谨一点,非native方法)
每个方法在执行的同时都会创建一个栈帧,其中包括:存储局部变量表,操作数栈,动态链接(指向当前方法所属的类的运行时常量池),方法出口。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
(1) 局部变量表
基本数据类型、对象引用、returnAddress类型。
基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。
局部变量表所需的内存空间在编译器完成分配。当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
(2)操作数栈
操作栈,它是一个后入先出栈(LIFO),栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
(3)动态链接
在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池。
Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态连接(Dynamic Linking)。
这些符号引用一部分会在类加载阶段或者第一次使用时就直接转化为直接引用,这类转化称为静态解析。另一部分将在每次运行期间转化为直接引用,这类转化称为动态连接。
(4) 方法返回
当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。(考虑正常返回,异常返回)
(5) 附加信息
虚拟机规范允许具体的虚拟机实现增加一些规范中没有描述的信息到栈帧之中,例如和调试相关的信息,这部分信息完全取决于不同的虚拟机实现。在实际开发中,一般会把动态连接,方法返回地址与其他附加信息一起归为一类,称为栈帧信息。
3、本地方法栈 线程私有
Native Method Stack与虚拟机栈的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法。
备注:HotSpot直接把本地方法栈和虚拟机栈合二为一。本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常
4、堆 线程共享
Java Heap是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此区域的唯一目的就是存放对象实例(Java虚拟机规范中的描述时:所有的对象实例以及数组都要在堆上分配)。
这里举例说明,对于数组,数组中每个元素的值是在堆内存中存储,而对于数组的引用是在JAVA栈中。
Java堆是GC的主要区域,因此很多时候也被称为GC堆。
从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)
从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为:新生代和老年代,在细致一点的有Eden空间,From Survivor空间,To Survivor空间等。
备注:有OOM异常
5、方法区 线程共享
Method Area是各个线程共享内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。 //https://www.runoob.com/java/java-string-intern.html
在HtoSpot虚拟机中该区域叫永久代,即方法区是虚拟机规范,而永久代是HotSpot实现的方法区。// ???
参考
https://www.cnblogs.com/dolphin0520/p/3613043.html
https://blog.csdn.net/suifeng629/article/details/82462164
https://blog.csdn.net/qzqanzc/article/details/81008598
二、HotSpot
待续。。。