1. 虚拟机执行引擎包括:
JIT即时编译器,解释器,GC等
执行引擎实则处理的是当前虚拟机栈,栈顶的栈帧,使用当前线程的程序的寄存器,找到对应的字节码指令,然后给执行引擎,通过与堆内存,方法区进行交互,和操作数栈进行工作 。
栈顶缓存技术:当前栈顶的栈帧中元素会被放在CPU寄存器中,以提高运行效率
1.2 栈帧结构
(1) 局部变量表
是栈帧的组成部分,存放着当前方法中的变量,在编译时期就可以确定大小类似于数据的结构但是下标是从1开始,0号存储的是this。
槽位复用对垃圾回收的影响:当一个局部变量表中的引用执行堆内存分配的空间在使用完成之后,如果没有被覆盖,可能不会被回收,因为站上的引用还是指向堆中的对象。注意槽位复用得是在方法中写代码块表示已经出了作用域,代码块的内存可复用一个。
(2)操作数栈
这里的栈是基于数组形式的栈,其深度大小在编译时期就可以确定,执行引擎也被叫做基于栈的执行引擎这里的栈指的就是操作数栈。
(3)动态链接
由于在编译时期的引用有的执向的是符号引用,在运行时候通过常量池将其转换为真正的引用。常量池:就是存放class文件的有关信息,如果没有常量池,将所有的信息都放在字节码中表示,可能太大。
(4)方法返回地址
(5)其他信息
1.3 方法调用
方法调用不等同于方法执行,方法调用的目的就是为了确定方法执行的版本即执行哪个方法
(1)解析
在编译时期就可以确定:在解析阶段将其的符号引用对应常量池转换为直接引用
如 private方法 ,static方法 构造方法等
(2)分派(确定执行哪个方法)
1. 静态分派:对应的就是方法的重载,依据静态类型确定调用哪个方法,静态分派发生在解析阶段 。
2. 动态分派:对应的就是方法的重写,动态分派的实现是,在方法区域都对应一个虚方法表,在运行时候解析出元素所指向的实际对象,然后去查找虚方方法表进行执行。
2. 基于字节码的解释执行引擎
解释执行就是类加载器所加载的class文件是经过javac编译器编译而成,javac输出的指令流是一种:基于栈的指令集架构,但是还有另一种就是基于寄存器的指令集架构。
类加载器将字节码加载进入内存然后执行引擎的解释器执行,执行引擎里面还有JIT即时编译器,其存在的意义就是如果有的重复的代码高频度执行,就有必要执行编译为机器码,以提高执行效率,如何确定那些代码是热点代码(高频度执行),这就需要热点代码探寻主要有两种方式
①采样法:周期性的检查栈的栈顶方法,如果经常出现在栈顶则就是热点代码
②计数器:为每一个方法维护计数器,主要用方法调用计数器,和回边计数器(主要作用是对循环代码块的检测)。
当调用一个方法的时候先判断该方法有没有被JIT编译(编译后的代码会被存放在方法区),如果被编译则执行编译的代码,如果没有则方法计数器加一,当方法计数器达到一定值的时候(阈值客户端是1500次,服务端是10000次)也可以通过虚拟机参数来设定,会被JIT编译。之后之后方法的调用入口会被换成新编译之后的值(栈上替换:方法的调用入口被改变)。调用计数器的值并不是绝对的被调用的次数,因为当方法一段时间不被调用之后会衰减,这个时间段被称为半衰期。也可以通过虚拟机参数将其设置为被调用的绝对次数。
2.1一些编译优化技术
(1)分层编译:将编译分为多个层次,分情况使用
图片.png
(2)栈上替换:将方法调用入口改变
(3)方法内联:避免真实的放法调用,将调用方法的过程去除。
(4)逃逸分析:分析对象的作用域,当一个对象在方法中被调用之后,如果在方法外部被引用,如作为返回值参数返回,这种称为方法逃逸。针对逃逸分析的两种优化。
①栈上分配:如果证明一个对象作用域不会超过该方法的作用域,可将对象直接分配在栈上,Hotspot中暂时没有实现。
②标量替换:标量:一个数据无法被分解为更小的对象,例如java中的原始数据类型,与标量对应的是聚合量,就是多个标量的复合。如果逃逸分析证明对象不会被外部引用,就可以将聚合量进行拆分,只留下在本方法中有用的量,也可以说不创建这个对象让对象的成员变量直接在栈上分配。
网友评论