(2)运行时数据区(我们核心关注这里的栈、堆、方法区)【这里面所存放的是类加载加载后的一些数据】
(3)执行引擎---是从内存中(可以任务是运行时数据区)获取相关数据来执行,执行后在把数据返还到内存中
1.解释器 :运行字节码文件
2. 编译器:把字节码文件(.class文件)编译成根据当前操作系统能够执行的文件,他决定跨平台
3. 垃圾回收器:会把内存中产生的垃圾数据进行回收清理,进行释放内存,清空时会根据jvm中垃圾回收算法进行回收,且自动进行回收,我们主要调优就是调这里,通过合适的算法进行对jvm的优化处理,避免出现卡顿,内存溢出等情况,尽可能的高效运行
4.JIN:(java本地接口)—怎么调用C语言的这些代码
5.本地方法库:本地方法栈为虚拟机使用到的 native 方法服务
类加载子系统
类加载器子系统---执行.class编译后的文件,载入到jvm的体系中,然后进行初始化的操作,把相关的数据放到内存中的某些区域,做具体的内存分配的操作。
运行时数据区
概述:
详细分析:
程序计数器(pc register):
PC计数器是一块较小的空间,它可以看做是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基本功能都需要依赖这个计数器来完成。
java虚拟机采用的是线程轮流切换、分配处理执行时间的方式来实现多线程的运行,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计时器互不影响,为线程私有。
如果线程执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果是一个本地方法,这个计数器值应该为空(undefined)。
Java虚拟机栈:
和pc计数器一样,栈也是线程私有的,它的生命周期和线程相同。每个方法执行时,虚拟机会同时创建一个栈帧用于存储局部变量表、操作数栈,动态链接、方法出口等信息。
局部变量表:
局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量表。在文件被编译时,方法的code属性中的max_locals数据项就确定了该方法所需分配的局部变量表的最大容量。
局部变量表的容量以变量槽(Variable Slot)为最小单位,其中boolean、byte、char、short、int、float、reference或者return Address类型的数据,这8中占一个Slot。而double、long则采用两个Slot。
当一个方法被调用时,Java虚拟机会使用局部变量表来完成参数值到参数变量列表的传递过程,即实参到形参的传递。如果执行的方法是实例方法(没有被static修饰的方法),那么局部变量中第0位索引的变量槽默认是用于传递所属方法所属对象实例的引用,可以通过关键字“this”来访问这个隐含的参数。其余参数则按照参数表顺序排列,占用从1开始的变量槽。
操作数栈:
操作数栈主要用于保存计算过程的中间过程,同时作为计算过程中变量临时的存储空间。 操作数栈中元素的数据类型不许与字节码指令的序列严格匹配,如:在计算过程中不能出现一个long和一个float使用iadd命令相加的情况。
动态链接:
1、每一个栈帧当中都包含以个指向运行时常量池重该栈帧所属方法的引用(invokedynamic指令)
2、在java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池里;比如:描述一个方法调用的另外的其它方法时,就是通过常量池中指向该方法的符号引用来表示,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
Java堆:
java世界里“几乎“”所有的对象实例都在这里分配内存。《java虚拟机规范》中的描述为:“所有的对象实例以及数组都应当在堆上分配”。堆也是垃圾收集器管理的内存区域,也可称为“GC堆”。
根据《java虚拟机规范》中的规定,java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
方法区:
方法区和堆一样,是各个线程所共享的内存区域,它用于存储:①被虚拟机加载的类型信息、②常量、③静态变量、④及时编译后的代码缓存。这个区域的内存回收的主要是针对常量池的回收和对类型的卸载。
静态常量池和运行时常量池
1)所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
2)而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
使用PC寄存器存储字节码指令地址有什么用呢?为什么使用PC寄存器记录当前线程的执行地址?
因为cpu需要不停的切换各个线程,这个时候切换回来之后,就得直到接着从哪儿开始继续执行。
JVM的字节码解释器就是通过改变PC寄存器的值来明确下一条指令应该执行什么样的字节码指令。
堆、栈、方法区的交互关系
对象的实例化和访问定位:
对象的定位访问:
句柄池中的句柄地址稳定,当对象的引用发生改变时,变化的只有句柄中的指针地址。
直接指针访问方式的速度很快,直接指向了对象的实例,节省了一次指针定位的时间开销。(Hotspot默认对象访问定位方式)