执行引擎
JVM运行Java程序的一套子系统
解释器
模板解释器和字节码解释器都是JVM执行引擎中的一种实现方式,用于解释执行Java字节码指令。
字节码解释器
Java字节码-> C++代码->硬件编码
字节码解释器是JVM默认的执行引擎,其将Java字节码指令解释为对应的本地机器指令,然后交给CPU执行。
在解释执行时,字节码解释器需要不停的解析和执行指令,效率相对较低。
模板解释器
Java字节码->硬件编码
模板解释器则是使用预编译的机器码来替代每次执行时的解释,从而提高执行效率。
模板解释器的基本思想是将频繁使用的Java方法编译成本地机器码并缓存下来,
以后每次执行这个方法时直接调用缓存中的机器码,避免了解释执行带来的开销。
底层实现过程
- 申请一块内存:可读可写可执行。
- 将处理new字节码的硬编码拿过来
- 将处理new字节码的硬编码写入申请的内存。
- 申请一个函数指针,用这个函数指针执行这块内存。
- 调用的时候,直接通过这个指针调用。
三种运行模式
- -Xint
- -Xcomp
- -Xmixed
运行模式 | 特点 | 优点 | 缺点 |
---|---|---|---|
客户端模式 | 针对交互式应用程序的默认模式,强调的是优秀的用户交互和较短的启动时间。 | 优秀的用户交互,较短的启动时间。 | 较低的性能,较高的内存消耗 |
服务器模式 | 针对长时间运行的服务器应用程序的默认模式,强调的是程序长时间稳定运行期间的高性能和稳定性。 | 较高的性能和稳定性。 | 较慢的启动时间 |
混合模式 | 混合客户端和服务器模式,根据应用程序的实际运行情况进行动态切换。 | 结合了客户端和服务器模式的优点,可以根据应用程序的实际情况进行动态切换。 | 初始启动时间较慢,切换时有性能损失。 |
编译器
正常的C++函数生成的硬编码都有堆栈操作。
字节码解释器
解释执行的
和编译器没关系模板解释器
执行的硬编码是即时编译器编译的。
JIT
即时编译器
- C1
c1编译器在client模式下的即时编译器
- 比C2搜集的数据少。触发宽松。
- 编译优化比较浅。
- C1编译器生成的代码执行效率低。
- C2
C2编译器是server模式下的即时编译器
- 触发条件比较严格,程序运行一段时间后才执行。
- 优化比较深。
- 编译生成的代码比C1效率高
混合编译器
- 即时编译触发条件
即时编译的最小单位是代码块。
- 方法被多次调用:当一个方法被调用多次之后,JIT编译器会根据统计信息来判断是否对该方法进行编译。
- 热点代码:如果某一段代码被多次执行,且执行时间占据了总执行时间的一定比例,那么JIT编译器会将这段代码编译成本地代码,以提高其执行效率。
- 被频繁调用的循环体:如果某个循环体被多次执行,且执行时间占据了总执行时间的一定比例,那么JIT编译器会将该循环体编译成本地代码,以提高其执行效率。
PS:反射调用:反射调用是一种运行时动态调用的方式,它会对JIT编译器造成困难,因为编译器难以在编译时确定要调用的方法和对象类型。
因此,在进行反射调用时,JIT编译器可能不会进行编译,以避免出现错误。
N热度:
生成热点代码
client,N默认是1500
server,N默认是10000热度衰减: new 7000
一段时间没执行后会2倍速递减 -> 3500
JIT编译的经典故障
业务增长,加节点。热机切冷机,冷机还未进行JIT编译,效率不及热机。导致请求延迟。
冷机:刚运行不久
热机:运行了一段时间
分析:
加节点时:相同配置节点加入,负载均衡平摊压力。 热机有热点代码缓存了,能承载的并发更大。
由于热机能承受的并发大于冷机。冷机一边在增加流量一遍在即时编译。性能就降低。在双重因素的
影响下,冷机反应速度降低。造成请求超时。
问题:热点代码缓存在哪里?
热点代码缓存在方法区: CodeCache(调优方向之一)
可以配置热点代码的大小,server模式是: 2496 2M,client模式下160k
即时编译是如何运行的
JIT编译过程一般分为三个阶段:
- 解释执行阶段:程序刚开始执行时,字节码解释器会逐行解释执行字节码指令,同时记录下被频繁执行的热点代码。
- 编译准备阶段:当某段代码被执行的次数超过一定的阈值时,JIT编译器会将该段代码标记为“热点代码”,并对其进行编译准备工作,包括类型推断、方法内联、数据流分析等。
- 编译执行阶段:JIT编译器将编译准备好的代码转换成本地机器代码,并执行该代码。此时程序的执行效率会有明显的提升。
- VM_THREAD
VM_THREAD 是 HotSpot 虚拟机中用来表示一个 Java 线程的数据结构。在 HotSpot 虚拟机内部,VM_THREAD
类型的变量主要被用来存储 Java 线程的状
态和相关信息,例如线程 ID、当前 Java 方法栈帧的指针、线程调用栈的栈帧信息、局部变量、操作数栈、返回值等等。
热点数据触发后,队列存放既时编译任务,当触发即时编译时会将编译任务放到及时编译队列里。
其顺序:
1、 触发既时编译任务入队列。
2、 VM_THREAD执行队列任务。
JIT调优
- 即时编译的线程有多少,如何调优?
- 硬编码,热点代码 热点代码存在哪里?热点代码缓冲区在哪里?
热点代码缓冲区,在方法区。
- 逃逸分析
逃逸分析(Escape Analysis)是Java虚拟机(JVM)对代码进行分析的一种技术,
旨在确定对象的生命周期是否跨越了某些线程或方法的边界。
在这种技术中,JVM会尝试分析对象的创建和使用情况,以判断对象是否逃逸出了方法的作用域,
进而决定是否可以对对象进行栈上分配等优化。
逃逸分析的作用域是非局部变量。判断对象是否是局部变量,局部变量是非逃逸对象。非局部变量,逃逸,判断是否在
方法外,县城外被引用。如果没有则判断是逃逸。
- 基于逃逸技术,JVM开发了三种优化技术。
- 栈上分配
逃逸分析还可以判断出哪些对象的引用不会逃逸到方法的外部,这些对象可以直接分配在栈上。这个过程叫做栈上分配。通过栈上分配,可以进一步减少对堆的使用,减轻垃圾回收的压力,提高程序的性能。 - 标量替换
逃逸分析首先会将对象拆解成若干个基本数据类型,这些基本数据类型可以直接保存在栈上。这个过程叫做标量替换。通过标量替换,可以将原本存储在堆上的对象的部分或全部字段分配到栈上,从而减少对堆的使用,进而提高程序的性能。 - 锁消除
没竞争的锁,会消除掉。
问题
- 如何通过代码测试是栈上分配?
对象在堆区分配,对象在虚拟机栈上分配。
工具:HSDB - JDK8的栈上分配存在吗?
生成一个对象100W次,在栈上是不是有100W个。如果没有,就存在栈上分配,不发生GC的情况下。