注:主要参考自《分布式java应用:基础与实践》《深入理解Java虚拟机(第二版)》

1、两种执行方式:

  • 解释执行(运行期解释字节码并执行)

    • 强制使用该模式:-Xint
  • 编译为机器码执行(将字节码编译为机器码并执行,这个编译过程发生在运行期,称为JIT编译)
    • 强制使用该模式:-Xcomp,下面是两种编译模式
    • client(即C1):只做少量性能开销比高的优化,占用内存少,适用于桌面程序。
    • server(即C2):进行了大量优化,占用内存多,适用于服务端程序。会收集大量的运行时信息。

注意:

  • 32为机器默认选择C1,可在启动时添加-client或-server来指定,64位机器若CPU>2且物理内存>2G则默认为C2,否则为C1
  • Hotspot JVM执行代码的机制:对在执行过程中执行频率高的代码进行编译,对执行频率不高的代码继续解释执行

查看当前机器默认是client模式还是server模式,使用:"java -version"命令,如下

其中,mixed mode表示"解释执行+编译执行"的混合模式

2、解释执行

查看 第三章 类文件结构与javap的使用 中的inc()方法的执行

或者查看《深入了解java虚拟机(第二版)》P272-P275

3、编译执行

  • 编译的对象

    • 方法
    • 方法中的循环体
      • OSR编译:编译整段代码,但是只有循环体部分会执行机器码,其他部分还是解释执行
  • 触发条件(执行频率大于多少)
    • 方法调用计数器:方法被调用的次数

      • client:1500  server:10000
      • 该阈值可通过-XX:CompileThreshold来指定
      • 这里"方法调用的次数"是指一段时间(半衰周期)内的调用次数,如果半衰周期内,该次数没有达到阈值,则该次数减半。
        • -XX:-UseCounterDecay 关闭上述机制,即半衰周期的无穷大
        • -XX:CounterHalfLifeTime 半衰周期
    • 回边计数器:循环体内循环代码的执行次数(即for中代码的循环的次数)
      • client:13995  server:10700
      • 该阈值可通过-XX:OnStackReplacePercent(注意该OSRP只是一个计算回边计数阈值的中间值),回边计数阈值
        • client:CompileThreshold*OSRP/100
        • server:CompileThreshold*(OSRP-InterPreterProfilePercentage)/100
        • -XX:OnStackReplacePercent:140  InterPreterProfilePercentage:33
  • 方法编译执行
    • 解释器调用方法时,检查是否有已经存在的编译版本,如果有,执行机器码,如果没有,方法调用计数器+1,然后判断方法调用计数器是否超过阈值,若超过,进行编译,后台线程进行编译,前台线程继续解释执行(即不会阻塞),直到下一次调用方法时,如果编译好了,就直接执行机器码,如果没编译好,就解释执行。
  • 循环体编译执行
    • 解释器执行到循环体时,检查是否有已经存在的编译版本,如果有,执行机器码,如果没有,回边计数器+1,然后判断回边计数器是否超过阈值,若超过,进行编译,后台线程进行编译,前台线程继续解释执行(即不会阻塞),直到下一次执行到循环体时,如果编译好了,就直接执行机器码,如果没编译好,就解释执行。

4、C1优化

说明:关于全部的优化技术列表,查看《深入理解java虚拟机(第二版)》P346-P347

只做少量性能开销比高的优化,占用内存少,主要的优化包括:

  • 方法内联
  • 冗余消除
  • 复写传播
  • 消除无用代码
  • 类型继承关系分析(CHA,辅助)
  • 去虚拟化

4.1、方法内联、冗余消除、复写传播、消除无用代码

4.1.1、方法内联

方法内联含义:假设方法A调用了方法B,把B的指令直接植入到A中。

    static class B{
int value;
final int get() {
return value;
}
} public void foo() {
y = b.get();
//do something
z = b.get();
sum = y + z;
}

说明:在上述代码中,b是B的一个实例。

方法内联之后,

    public void foo() {
y = b.value;
//do something
z = b.value;
sum = y + z;
}

方法内联的条件:

  • get()编译后的字节数<=35byte(默认) -XX:MaxInlineSize=35指定

方法内联的地位:

  • 优化系列中最一开始使用的方式(因为是很多其他优化手段的基础)
  • 消除方法调用的成本(建立栈帧、避免参数传递、避免返回值传递、避免跳转)

4.1.2、冗余消除

冗余消除:如上边的两个b.value冗余(前提,在do something部分没有对b.value进行操作,这也是我们在做优化之前需要先收集数据的原因)

假设在do something部分没有对b.value进行操作,进行冗余消除后,

    public void foo() {
y = b.value;
//do something
z = y;
sum = y + z;
}

4.1.3、复写传播

当然,在冗余消除后,JIT对上述的代码进行分析,发现变量z没用(可以完全用y来代替),进行"复写传播"之后,

    public void foo() {
y = b.value;
//do something
y = y;
sum = y + y;
}

4.1.4、无用代码消除

在"复写传播"后,发现"y=y"是无用代码,所以可以进行"无用代码的消除"操作,消除之后,

    public void foo() {
y = b.value;
//do something
sum = y + y;
}

需要说明的是,这里的"无用代码的消除"是在前三部优化的基础上来做的,而javac编译中"语义分析"部分的"无用代码的消除"是直接消除一些直接写好的代码(例如:if(false){})

4.2、类型继承关系分析、去虚拟化

public interface Animal {
public void eat();
} public class Cat implements Animal{
public void eat() {
System.out.println("cat eat fish");
}
} public class Test{
public void methodA(Animal animal){
animal.eat();
}
}

首先分析Animal的整个"类型继承关系",发现只有一个实现类Cat,那么在methodA(Animal animal)的代码就可以优化为如下,

    public void methodA(Animal animal){
System.out.println("cat eat fish");
}

但是,如果之后在运行过程中,"类型继承关系"发现Animal又多了一个实现类Dog,那么此时就不在执行之前优化编译好的机器码了,而是进行解释执行,即如下的"逆优化"。

逆优化:

当编译后的机器码的执行不再符合优化条件,则该机器码对应的部分回到解释执行。

eg.比如"去虚拟化",如果编译之后,发现类的实现方法多于一种了,此时就要执行"逆优化"

5、C2优化

进行了大量优化,占用内存多,适用于服务端程序,对于C2优化,除了具有C1的优化措施后,还有很多优化。

逃逸分析(辅助):

开启:-XX:+DoEscapeAnalysis

根据运行状况来判断方法中的变量是否会被方法或外部线程所读取,若不会,此变量是不逃逸的。基于此,C2在编译时会做:

  • 标量替换:开启 -XX:+EliminateAllocations
  • 栈上分配
  • 同步削除:开启 -XX:+EliminateLocks

5.1、标量替换

含义:将一个java对象打散,根据程序,将该对象中的属性作为一个个标量来使用。

    Point point = new Point(1,2);
System.out.println("point.x:" + point.x + ",point.y:" + point.y);
//do after

若在//do after中(即前边两句代码之后的所有代码中)再没有其他代码访问"point对象"了,则将"point对象"打散并进行标量替换,

    int x = 1;
int y = 2;
System.out.println("point.x:" + x + ",point.y:" + y);

好处:

  • 如果对象中定义的所有变量有的并没有被用到,"标量替换"可以节省内存
  • 执行时,不需要寻找对象引用,速度会快

5.2、栈上分配

含义:确定一个方法的变量不会逃逸出当前方法之外(即该变量不会被其他方法引用),则该变量可以直接分配在栈上,随方法执行结束,栈帧消失,该变量也消失,减轻GC压力。

好处:

  • 执行时,不需要根据对象引用去堆中找对象,速度会快
  • 分配在栈上,随方法执行结束,栈帧消失,该变量也消失,减轻GC压力。
  • 使用栈上分配,必须开启标量替换

5.3、同步削除

含义:确定一个方法的变量不会逃逸出当前线程之外(即该变量不会被其他线程使用),则对于该变量的同步策略就消除掉,如下,

    synchronized(cat){
//do xxx
}

若cat不会逃逸出当前线程,则同步块可以去掉,如下,

//do xxx

总结:

解释器:

  • 程序启动速度比编译快
  • 节省内存(不需要编译,所以不需要放置编译后的机器码)

JIT编译器:

  • 时间长了,对于"热点代码"的执行会快

注意:

  • 使用JIT而不是使用在编译期直接编译成机器码,除了解释器部分的两条有点外,还为了在运行期收集数据,有目的的进行编译

第六章 字节码执行方式--解释执行和JIT的更多相关文章

  1. 字节码执行方式--解释执行和JIT

    此文已由作者赵计刚薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.两种执行方式: 解释执行(运行期解释字节码并执行) 强制使用该模式:-Xint 编译为机器码执行(将字 ...

  2. 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的

    概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...

  3. 深入了解java虚拟机(JVM) 第十三章 虚拟机字节码执行引擎

    一.概述 执行引擎是java虚拟机最核心的组成部件之一.虚拟机的执行引擎由自己实现,所以可以自行定制指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式.所有的Java虚拟机的执行 ...

  4. JVM-5.字节码执行引擎

    一.概述 二.栈帧结构 三.方法调用 四.方法执行       一.概述 虚拟机与物理机 虚拟机是一个相对于物理机的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件. ...

  5. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  6. 8、JVM--虚拟机字节码执行引擎

    8.1.概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上 ...

  7. 深入理解Java虚拟机(字节码执行引擎)

    深入理解Java虚拟机(字节码执行引擎) 本文首发于微信公众号:BaronTalk 执行引擎是 Java 虚拟机最核心的组成部分之一.「虚拟机」是相对于「物理机」的概念,这两种机器都有代码执行的能力, ...

  8. 深入理解JVM虚拟机5:虚拟机字节码执行引擎

    虚拟机字节码执行引擎   转自https://juejin.im/post/5abc97ff518825556a727e66 所谓的「虚拟机字节码执行引擎」其实就是 JVM 根据 Class 文件中给 ...

  9. 深入了解jvm-2Edition-虚拟机字节码执行引擎

    1.概述 Java虚拟机规范制定了虚拟机字节码执行引擎的概念模型,本章主要从概念模型层次来探究虚拟机的方法调用和字节码执行. 方法调用中,最核心的,是如何确定调用的方法,也就是方法的分派. 字节码执行 ...

随机推荐

  1. js 参数传递

    最近在读<javascript高级程序设计>时碰到了js传递方式的问题,花费了些时间,不过总算明白了. 数据类型 在 javascript 中数据类型可以分为两类: 基本类型值 primi ...

  2. thinkphp中如何是实现多表查询

    多表查询经常使用到,但如何在thinkphp中实现多表查询呢,其实有三种方法. 1 2 3 4 5 6 7 8 9 10 11 12 // 1.原生查询示例: $Model = new Model() ...

  3. elasticsearch-.yml(中文配置详解)

    此elasticsearch-.yml配置文件,是在$ES_HOME/config/下 elasticsearch-.yml(中文配置详解) # ======================== El ...

  4. hdu-3790最短路刷题

    title: hdu-3790最短路刷题 date: 2018-10-20 14:50:31 tags: acm 刷题 categories: ACM-最短路 概述 一道最短路的水题,,,尽量不看以前 ...

  5. Java反射机制demo(三)—获取类中的构造函数

    Java反射机制demo(三)—获取类中的构造函数 1,获取类中所有的构造函数 如下面的代码中所示,这个类中显式的构造函数有五个. 空构造: public UserInfo() 带参构造有四个: pu ...

  6. Swift2.0语言教程之类的嵌套与可选链接

    Swift2.0语言教程之类的嵌套与可选链接 Swift2.0语言类的嵌套 在一个类中可以嵌套一个或者多个类.它们的嵌套形式也是不同的,大致分为了两种:直接嵌套和多次嵌套.下面依次讲解这两种方式. S ...

  7. bzoj 4242 水壶 (多源最短路+最小生成树+启发式合并)

    4242: 水壶 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 1028  Solved: 261[Submit][Status][Discuss] ...

  8. HTTP 499 状态码 nginx下 499错误[转]

    日志记录中HTTP状态码出现499错误有多种情况,我遇到的一种情况是nginx反代到一个永远打不开的后端,就这样了,日志状态记录是499.发送字节数是0. 老是有用户反映网站系统时好时坏,因为线上的产 ...

  9. 【干货】PHP常见危险函数

    passthru() 功能描述:允许执行一个外部程序并回显输出,类似于 exec(). 危险等级:高 exec() 功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等). 危 ...

  10. 【洛谷】2473:[SCOI2008]奖励关【期望DP(倒推)】

    P2473 [SCOI2008]奖励关 题目背景 08四川NOI省选 题目描述 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不 ...