一:
运行时数据区的结构为:方法区、堆、虚拟机栈、本地方法栈、程序计数器。(前面两个是进程共享的,后面三个是线程私有的)
JVM的每个线程都与操作系统的本地线程直接映射,先java线程创建后本地线程。
阿里的结构如下:
1、程序计数器(程序计数寄存器):它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。(就是存储下一条指令的代码,它是可以确保线程上下文切换后恢复到正确的执行位置)
如果线程正在执行的是一个java方法,这个计数器记录的是当前方法,但是如果执行的是本地方法那么计数器值应该为空。
2、java虚拟机栈:描述的是java方法执行的线程内存模型。优点是跨平台,指令集小,编译器容易实现,缺点就是性能下降(进出栈)。
栈帧主要用于存储局部变量表、操作数栈、动态连接、方法地址等信息。
栈是运行时单位,堆是存储时单位;即栈解决程序的运行问题,程序如何执行,如何处理数据;堆则解决如何存储数据,数据放哪怎么放。
每个方法被执行的时候,java虚拟机栈都会同步创建一个栈帧(入栈)即一个方法对应一个栈帧,栈帧是一个内存区块是一个数据集,维系着这个方法执行过程中的各种数据信息,并且这个方法在栈顶也被称为当前方法,如果方法执行完成则会出栈。
(1)局部变量表:最基本的存储单元是Slot(槽),主要用于存储定义在方法体内的局部变量和存储方法参数,这些数据类型包括基本数据类型、对象引用,以及returnAddress类型;并且局部变量表是建立在线程上的,所以是不是存在数据安全问题(线程私有);局部变量表的容量大小是在编译期间确定下来的,运行期间是不会改变局部变量表的大小。(如果当前帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的再按照顺序,并且局部变量表的槽位是可以重复使用的)局部变量必须要显性赋值,否则编译报错,因为在编译阶段都会进行分配空间。(局部变量表太大也会导致stackoverflowererror)
(2)操作数栈:
(3)虚方法和非虚方法:虚方法要运行时候才能确定的就叫虚方法,非虚方法是编译时候就能确定版本并且运行时候不变的就是非虚方法
(4)方法返回值:调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。如果是异常退出,返回地址是要通过异常表来确定。正常和异常的区别是通过异常出口的退出的不会给他的上层调用者返回任何信息。