即时编译器

Java程序最初都是通过解释器进行执行,当发现某个方法或者代码块被运行得非常频繁,这些代码就被认为是热点代码,为了提高这些代码得运行效率,虚拟机会把热点代码编译成本地机器码,并进行优化,运行时完成这个任务的后端编译器被称为即时编译器

解释器与编译器

主流Java虚拟机内部同时包含解释器与编译器。

  • 解释器优点:当程序需要迅速启动和运行的时候,解释器可以省去编译的时间,立即运行代码。

  • 编译器优点:当程序启动后,编译器将执行频繁的代码编译成本地代码,减少解释器的中间损耗,提高执行效率。

  • 内存问题:编译器编译好的代码会占用本地内存,所以,如果内存不够,可以尽可能地利用解释编译,进行逆优化

编译对象和触发条件

热点代码

  • 被多次调用的方法。
  • 被多次执行的循环体。

热点代码的编译目标都是整个方法,而不是单独的一段代码。

编译时会传入方法的入口字节码序号,然后在编译后,直接替换字节码初的方法,这种编译过程被称为栈上替换

热点探测

检测某段代码是不是热点代码的行为被称为热点探测

主流的两种热点探测方法:

  • 基于采样的热点探测:周期性地去检查各个线程地调用栈顶,如果发现某个方法经常出现在栈顶,那么这个方法就是热点方法。
    优点:简单高效,容易去获取方法调用关系。
    缺点:很难精确计算一个方法的热度,容易受到干扰,如线程阻塞。
  • 基于计数器地热点探测:虚拟机为每个方法建立一个计数器,统计方法地调用次数,经常被调用就是热点代码。
    优点:精确计算每个方法的热度。
    缺点:额外的开销维护计数器,花费空间,不能获取方法的调用关系。

HotSpot使用计数器的方法,并设计了两种计数器:

  • 方法调用计数器:计算方法的调用次数。
  • 回边计数器:计算循环代码的次数。

编译过程

客户端编译器

三段式编译:

  1. 第一个阶段,将子节码转化成一种高级的中间代码表示。目的时为了更用以实现代码的优化。也完成了一部分基础的优化,如方法内联和传播优化。
  2. 第二个阶段,再从上一个阶段的代码转化为低级中间代码表示,完成空值检查消除、范围检查消除等。
  3. 最后阶段,使用线性扫描算法,为上一步产生的代码分配寄存器,并做窥孔优化,然后产生机器码。

即时编译优点

  1. 性能分析制导优化:分析代码的运行的情况,进行集中优化。
  2. 激进预测性优化:虚拟机会根据继承类关系分析等一系列激进的猜测去做虚拟化,预测程序之后的运行情况,再优化。
  3. 链接优化:主程序和各个动态链接库可以各自进行优化。

提前编译器

破坏了Java语言的平台无关性。但是在Android平台上很有优势。

提前编译优点

  1. 不需要在运行的时候占用资源。
  2. 本质是给即时编译器做了缓存加速,改善程序的启动时间。
  3. 提前编译的时候没有执行时间和资源限制的压力,可以没有顾忌地进行优化。

编译器优化技术

方法内联

  • 基本原理:将目标方法的代码转移到发起调用的地方,减少真实的方法调用。

需要通过类型继承关系分析,确认到底调用的是哪个方法。

逃逸分析

不是直接优化代码,而是一种为优化提供的分析技术。

  • 基本原理:一个对象再方法中被定义后,如果被外部方法调用,就是方法逃逸;如果被外部线程访问,就是线程逃逸。不逃逸、方法逃逸、线程逃逸,就是对象从低到高的逃逸程度。根据对象的逃逸程度再进行不同的优化手段。

优化方法:

  • 栈上分配:当确定一个对象不会被其他线程访问,就可以将对象分配到栈上,而不是堆上,这样可以减轻堆上垃圾回收的压力,让对象随着方法调用结束被销毁。支持方法逃逸,不支持线程逃逸。
  • 标量替换:如果确定一个对象不会在方法外被访问,就可以在实例化对象的时候,不创建对象,而是拆散成多个被这个方法调用成员变量来代替。不支持方法逃逸和线程逃逸。
  • 同步消除:如果确定一个变量不会回被其他线程访问,就可以消除对这个变量的同步措施,提高运行效率。

公共子表达式消除

  • 基本原理:如果一个计算表达式之前被计算过了,而且其中的变量没有被修改,那就称为公共子表达式。对于这种表达式就不在计算,直接用之前计算过的结果进行代替。

数组边界检查消除

在编译的时候就判断数组是否会越界,这样在运行的时候就不需要每次在读取数组值的时候进行越界判断了。

示例

  1. 初始代码
static class B {
int value;
final int get() {
return value;
}
}
public void foo() {
y = b.get();
// ...do stuff...
z = b.get();
sum = y + z;
}
  1. 方法内联

public void foo() {
y = b.value;
// ...do stuff...
z = b.value;
sum = y + z;
}
  1. 冗余存储消除

public void foo() {
y = b.value;y
// ...do stuff...
z = y;
sum = y + z;
}
  1. 复写传播

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

4.无用代码消除


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

深入理解Java虚拟机(九)——后端编译与优化的更多相关文章

  1. 深入理解Java虚拟机(程序编译与代码优化)

    文章首发于微信公众号:BaronTalk,欢迎关注! 对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就 ...

  2. 深入理解Java虚拟机之自己编译JDK

    题外话 最近在阅读<深入理解Java虚拟机>,其中有一小节实战是自己编译JDK,实际操作下来后遇到问题不少,为此特地记录,也希望可以给大家带来一些参考! 前置准备 平台及工具:Window ...

  3. 深入理解Java虚拟机 #01# 自己编译JDK

    x 首先用书上的脚本尝试,失败. 之后根据源文件的 README 编译,抛出: root@linux:/opt/openjdk# sh ./get_source.sh ERROR: Need init ...

  4. 《深入理解java虚拟机》学习笔记之虚拟机即时编译详解

    郑重声明:本片博客是学习<深入理解java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块 ...

  5. 《深入理解java虚拟机》学习笔记之编译优化技术

    郑重声明:本片博客是学习<深入理解Java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序员有一个共识,以编译方式执行本地代码比解释方式更快,之所以有这样的共识,除去虚拟机解释 ...

  6. 深入理解Java虚拟机--下

    深入理解Java虚拟机--下 参考:https://www.zybuluo.com/jewes/note/57352 第10章 早期(编译期)优化 10.1 概述 Java语言的"编译期&q ...

  7. 《深入理解 Java 虚拟机》笔记整理

    正文 一.Java 内存区域与内存溢出异常 1.运行时数据区域 程序计数器:当前线程所执行的字节码的行号指示器.线程私有. Java 虚拟机栈:Java 方法执行的内存模型.线程私有. 本地方法栈:N ...

  8. 《深入理解Java虚拟机》第 3 版里面到底多了哪些知识点?本文竟然得到了本书作者的认可!

    这是why的第 47 篇原创文章 荒腔走板 大家好,我是 why.老规矩,先是简短的荒腔走板聊聊生活. 上面的图是前几天拍的,那天晚上下班后,刚刚走进小区就看到了这一轮弯月和旁边那一颗特别特别亮的星星 ...

  9. JVM | 第1部分:自动内存管理与性能调优《深入理解 Java 虚拟机》

    目录 前言 1. 自动内存管理 1.1 JVM运行时数据区 1.2 Java 内存结构 1.3 HotSpot 虚拟机创建对象 1.4 HotSpot 虚拟机的对象内存布局 1.5 访问对象 2. 垃 ...

随机推荐

  1. 【涂鸦物联网足迹】涂鸦云平台消息服务—顺带Pulsar简单介绍

    前序系列文章>>> [涂鸦物联网足迹]涂鸦云平台标准指令集 开放消息平台主要通过 Pulsar 主动推送各种事件数据给外部合作伙伴,以满足合作伙伴对消息实时性和消息持久化的要求. 一 ...

  2. SMTPAuthenticationError: (535, '5.7.8 authentication failed')解决办法

    代码: 运行之后一直报错,SMTPAuthenticationError: (535, '5.7.8 authentication failed'). 查找半天是因为新浪邮箱的STMP的服务没有开启, ...

  3. MySQL 四种隔离级别详解,看完吊打面试官

    转发链接:https://zhuanlan.zhihu.com/p/76743929 什么是事务 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就 ...

  4. Python相比其他计算机语言真的更有优势吗?

    要了解Python相比其他计算机语言,首先要了解Python语言的特点 Python语言的特点 一.简单易学.明确优雅.开发速度快 ①简单易学:与 C 和 Java 比,Python的学习成本和难度曲 ...

  5. 这几种实现线程的方法你一定要知道,月薪20k以上的面试都会问到

    实现线程的三种方式总结 最近有看到Java线程的实现相关问题,在此对线程实现方式做一个小小的总结,当做笔记,便于日后查看. 平时常用的线程方式有三种: (1).继承Thread类,并重写其run()方 ...

  6. django清理migration终极解决办法

    1.django生成数据表结构的过程 在我们设计好models以后,我们可以通过以下命令生成将要同步给数据库的数据结构文件 python manage.py makemigrations 生成的文件在 ...

  7. Android light系统分析

    光线系统包括:背光,闪光,led指示灯   一.内核层     Led-class.c (kernel-3.10\drivers\leds) 这个文件给HAL层提供接口     led_brightn ...

  8. 在Guitar Pro中如何模拟电子管音响

    在这篇文章中,我们将使用Guitar Pro 7中的功能和工具,完成构建一个真实的电子管吉他音箱调音过程. 虽然Guitar Pro是用于创建吉他乐谱的工具,但在新版本中(主要是6和7)它也有一些模拟 ...

  9. FL Studio CPU面板讲解

    在FL Studio中,其CPU面板主要是由CPU使用表.内存使用表和复音数这几个部分组成的.这些对刚接触这款音乐制作软件的同学来说是非常陌生的吧!因为不知道这些是什么,主要的作用是什么.所以小编这里 ...

  10. Java中对象在内存中的大小、分配等问题

    Java创建一个对象的过程 是否对象指向的类已经加载到内存了 如果没有加载,就要经过load.linking(verification.preparation.resolution).initiali ...