传统JIT和java9新特性AOT理解
java慢的原因
1. 除了少量基本类型用栈存储外,所有对象都使用堆存储。堆的性能低于栈。
2. 很多强制类型转换(cast)或加查,耗用内存大。java运行时对类型检测,如果类型不正确会抛出ClassCastException异常。
3. 自动垃圾回收机制要耗用不少内存。
JRE带来的跨平台性:
Java 平台程序表示的一个重要部分是字节码序列,它描述了 Java 类中每个方法所执行的操作。字节码使用一个理论上无限大的操作数堆栈来描述计算。这个基于堆栈的程序表示提供了平台无关性,因为它不依赖任何特定本地平台的 CPU 中可用寄存器的数目。可在操作数堆栈上执行的操作的定义都独立于所有本地处理器的指令集。Java 虚拟机(JVM)规范定义了这些字节码的执行(参见 参考资料)。执行 Java 程序时,用于任何特定本地平台的任何 JRE 都必须遵守 JVM 规范中列出的规则。
本地编译本质上是特定于平台的。那么 Java 平台如何在不牺牲平台无关性的情况下实现本地编译的性能?答案就是使用 JIT 编译器进行动态编译,这种方法已经使用了十年。
JIT(动态编译)和AOT(静态编译):
Java8传统的JIT执行流程:
一个java文件的执行流程:
但此编译器与通常说的javac那个编译器不同,它其实是将字节码编译为硬件可执行的机器码的。
1、 源代码经javac编译成字节码,class文件(前端编译:也即把满足Java语言规范的程序转化为满足JVM规范所要求格式的功能)
优点:
这阶段的优化是指程序编码方面的;
许多Java语法新特性(java5"语法糖":泛型、内部类等等),是靠前端编译器实现的,而不是依赖虚拟机;
编译成的Class文件可以直接给JVM解释器解释执行,省去编译时间,加快启动速度;
2、 程序字节码经过JIT环境变量进行判断,是否属于“热点代码”(多次调用的方法,或循环等)
3、 如是,走JIT编译为具体硬件处理器(如sparc、intel)机器码
通过Java虚拟机(JVM)内置的即时编译器(Just In Time Compiler,JIT编译器);在运行时把Class文件字节码编译成本地机器码的过程;
优点:
通过在运行时收集监控信息,把"热点代码"(Hot Spot Code)编译成与本地平台相关的机器码,并进行各种层次的优化;
可以大大提高执行效率;
缺点:
收集监控信息影响程序运行;
编译过程占用程序运行时间(如使得启动速度变慢);
编译机器码占用内存;
4、 如否,则直接由解释器解释执行
5、 操作系统及类库调用
6、 硬件
以上实际上是JVM的“混合模式”对java程序的执行方式。
以上整个流程就属于JIT动态编译的过程:
补充:
由于动态编译技术的多项改进,在很多应用程序中,现代的 JIT 编译器可以产生与 C 或 C++ 静态编译相当的应用程序性能。但是,仍然有很多软件开发人员认为 —— 基于经验或者传闻 —— 动态编译可能严重干扰程序操作,因为编译器必须与应用程序共享 CPU。一些开发人员强烈呼吁对 Java 代码进行静态编译,并且坚信那样可以解决性能问题。对于某些应用程序和执行环境而言,这种观点是正确的,静态编译可以极大地提高 Java 性能,或者说它是惟一的实用选择。但是,静态地编译 Java 应用程序在获得高性能的同时也带来了很多复杂性。一般的 Java 开发人员可能并没有充分地感受到 JIT 动态编译器的优点。
Java8为什么采用混合模式:
对于解释执行,不经过jit直接由解释器解释执行所有字节码,执行效率不高。 而编译执行不加筛选的将全部代码进行编译机器码不论其执行频率是否有编译价值,在程序响应时间的限制下,编译器没法采用编译耗时较高的优化技术(因为JIT的编译是首次运行或启动的时候进行的!),所以,在纯编译执行模式下的java程序执行效率跟C/C++也是具有较大差距的。
因此,新版本的jvm默认都是采用混合执行模式。
编译执行和解释执行的区别:
1. 解释程序:
解释程序是高级语言翻译程序的一种,它将源语言(如BASIC和java得到的class字节码文件)书写的源程序作为输入,解释一句后就提交计算机执行一句,并不形成目标程序。就像外语 翻译中的“口译”一样,说一句翻一句,不产生全文的翻译文本。如在终端上打一条命令或语句,解释程序 就立即将此语句解释成一条或几条指令并提交硬件立即执行且将执行结果反映到终端,从终端把命令打入后,就能立即得到计算结果。
总结:源程序输入到计算机后,解释程序将程序逐句翻译,翻译一句执行一句边翻译边执行,不产生目标程序。
解释方式是按照远程序中语句的动态顺序,直接地逐句进行分析解释,并立即执行。
解释的过程:在程序执行时开始翻译代码为二进制,翻译一句执行一句。
2.编译程序:
这是一类很重要的语言处理程序,它把高级语言源程序作为输入,进行翻译转换,产生出机器语言的目标程序,然后再让计算机去执行这个目标程序,得到计算结果。
编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化,存储分配和代码生成。为了完成这些分析综 合任务,编译程序采用对源程序进行多次扫描的办法,每次扫描集中完成一项或几项任务,也有一项任务分散到几次扫描去完成的。
在实际应用中,对于需要经常使用的有大量计算的大型题目,采用执行速度较快的编译型的高级语言较好,虽然编译过程本身较为复杂,但一旦形成目标文件,以后可多次使用。相反,对于小型题目或计算简单不太费机时的题目,则多选用解释型的会话式高级语言,如BASIC,这样可以大大缩短编程及调试的时间。
总结: 编译方式把源程序的执行过程严格地分成两大步:编译和运行。
1.把源程序全部翻译成目标代码
2.运行目标代码,获取执行结果。
动态编译的缺点:
但是,动态编译确实具有一些缺点,这些缺点使它在某些情况下算不上一个理想的解决方案。例如,因为识别频繁执行的方法以及编译这些方法需要时间,所以应用程序通常要经历一个准备过程,在这个过程中性能无法达到其最高值。在这个准备过程中出现性能问题有几个原因。首先,大量的初始编译可能直接影响应用程序的启动时间。不仅这些编译延迟了应用程序达到稳定状态的时间(想像Web 服务器经历一个初始阶段后才能够执行实际有用的工作),而且在准备阶段中频繁执行的方法可能对应用程序的稳定状态的性能所起的作用也不大。如果JIT 编译会延迟启动又不能显著改善应用程序的长期性能,则执行这种编译就非常浪费。虽然所有的现代JVM 都执行调优来减轻启动延迟,但是并非在所有情况下都能够完全解决这个问题。
其次,有些应用程序完全不能忍受动态编译带来的延迟。如 GUI 接口之类交互式应用程序就是这样的例子。在这种情况下,编译活动可能对用户使用造成不利影响,同时又不能显著地改善应用程序的性能。
最后,用于实时环境并具有严格的任务时限的应用程序可能无法忍受编译的不确定性性能影响或动态编译器本身的内存开销。
因此,虽然 JIT 编译技术已经能够提供与静态语言性能相当(甚至更好)的性能水平,但是动态编译并不适合于某些应用程序。在这些情况下,Java代码的提前(Ahead-of-time,AOT)编译可能是合适的解决方案。
Java9新特性AOT:
Java 语言本地编译应该是为传统语言(如 C++ 或 Fortran)而开发的编译技术的一个简单应用。传统的静态语言的模式
能够将class文件直接编译成可执行二进制文件。
可以通过谨慎地使用 AOT 编译代码加快应用程序启动,因为虽然这种代码通常比 JIT 编译代码慢,但是却比解释代码快很多倍。此外,因为加载和绑定AOT 编译代码的时间通常比检测和动态编译一个重要方法的时间少,所以能够在程序执行的早期达到那样的性能。类似地,交互式应用程序可以很快地从本地代码中获益,无需使用引起较差响应能力的动态编译。
只有在执行代码引用类的时候才加载该类。因为是在程序执行前进行AOT 编译的,所以编译器无法预测加载了哪些类。就是说编译器无法获知任何静态字段的地址、任何对象的任何实例字段的偏移量或任何调用的实际目标,甚至对直接调用(非虚调用)也是如此。
设想一个正在进行bug 修复的开发环境:类文件的内容可能随不同的应用程序的执行而变化。此外,Java代码可能在程序执行前根本不存在:比如Java 反射服务通常在运行时生成新类来支持程序的行为。
缺少关于静态、字段、类和方法的信息意味着严重限制了 Java 编译器中优化框架的大部分功能。
虽然AOT 编译号称动态编译缺点的万能解决方案,但是由于Java 语言本身的动态特性,它也面临着提供本地编译全部潜能的挑战。
牺牲了平台无关性和代码,它们不能利用程序的动态行为!
JIT(动态编译)和AOT(静态编译)的比较:
JIT(Just in time:即时编译器、动态编译):
在运行是动态编译,可以只编译那些经常使用的方法(热方法),提高效率;其它的解释。
根据代码结构肯能提高或降低java程序执行速度。(代码结构好提高:某些情况下可比肩C++,不然降低)。
运行时进行本地代码编译而不是在程序运行前编译(C、C++是这种),保证了可移植性。
AOT(Ahead of time:提前编译、静态编译):
运行前编译好,缺点是全编译了,不用的也编译了,牺牲了平台无关性和代码质量,因为它们不能利用程序的动态行为,也不具有关于加载的类或类层次结构的信息。
避免 JIT 编译器的运行时性能消耗或内存消耗,或者避免解释程序的早期性能开销。极大提高java代码性能。
-Xint, -Xcomp, 和-Xmixed
-Xint和-Xcomp参数和我们的日常工作不是很相关,但是我非常有兴趣通过它来了解下JVM。在解释模式(interpreted mode)下,-Xint标记会强制JVM执行所有的字节码,当然这会降低运行速度,通常低10倍或更多。-Xcomp参数与它(-Xint)正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码,从而带来最大程度的优化。这听起来不错,因为这完全绕开了缓慢的解释器。然而,很多应用在使用-Xcomp也会有一些性能损失,当然这比使用-Xint损失的少,原因是-xcomp没有让JVM启用JIT编译器的全部功能。JIT编译器在运行时创建方法使用文件,然后一步一步的优化每一个方法,有时候会主动的优化应用的行为。这些优化技术,比如,积极的分支预测(optimisticbranch prediction),如果不先分析应用就不能有效的使用。另一方面方法只有证明它们与此相关时才会被编译,也就是,在应用中构建某种热点。被调用很少(甚至只有一次)的方法在解释模式下会继续执行,从而减少编译和优化成本。
注意混合模式也有他自己的参数,-Xmixed。最新版本的HotSpot的默认模式是混合模式,所以我们不需要特别指定这个标记。我们来用对象填充HashMap然后检索它的结果做一个简单的用例。每一个例子,它的运行时间都是很多次运行的平均时间。
参考文章:
https://blog.csdn.net/bfboys/article/details/54171368
https://blog.csdn.net/Hsuxu/article/details/9320699
https://www.baidu.com/link?url=YEo4jCyN3H7WQ6VNdE8jgpZ3-NntcSuqNRDMStUTHP3wkjJWIp8HtvjKlkAwhCOZKIwHRgyIdykGf63hhqKH8_&wd=&eqid=a244deff000695ff000000055af19ee7
https://blog.csdn.net/bfboys/article/details/54171368
https://blog.csdn.net/qq280929090/article/details/79507323
感谢!
传统JIT和java9新特性AOT理解的更多相关文章
- Java9 新特性 详解
作者:木九天 < Java9 新特性 详解 > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...
- 点击--》java9 新特性 详解
引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...
- 【Java基础】Java9 新特性
Java9 新特性 模块化系统 Java 和相关生态在不断丰富的同时也越来越暴露出一些问题: Java 运行环境的膨胀和臃肿.每次 JVM 启动的时候,至少会 30-60MB 的内存加载,主要原因是 ...
- Java9新特性之——JShell
java9已经在北京时间9月22日正式发布,开发者可以在oracle jdk官网上下载到最新的jdk9.jdk9和jdk8中的新特性不同:jdk8中的stream和lambda表达式能够让开发者非常快 ...
- Java9 新特性
Java9中的9个新特性 1. Java 平台级模块系统 2. Linking 3. JShell: 交互式 Java REPL 4. 改进的 Javadoc 5. 集合工厂方法 6. 改进的 Str ...
- JAVA9新特性——JShell使用笔记
前言:JShell是java 9的新特性之一,由于在之前学校java8的lambda表达式的时候,就希望有这么一个可以交互的界面来快速响应编写的代码.刚好java9出来了,就对它把玩起来了... 内容 ...
- 适应c++ 新特性 - 与我 - 多年传统方式开发(新特性参考微软标准:https://msdn.microsoft.com/zh-cn/library/hh279654.aspx)
公司同事都在积极使用c++的新特性,并对其赞不绝口,而自己一直做着传统的c++开发方式,到底这些新特性如何,又是怎么提高开发效率的,我依然在疑问当中,从同事的说法和实际代码操练里,确实在减少代码量,集 ...
- Java9新特性
转载:http://blog.csdn.net/qq_32524177/article/details/77014757 写在前面的话:Java9来了,搜索了很多关于Java9的新特性,但文献不多,特 ...
- java9新特性-6-多版本兼容jar包
1.官方Feature 238: Multi-Release JAR Files 2.使用说明 当一个新版本的Java出现的时候,你的库用户要花费数年时间才会切换到这个新的版本.这就意味着库得去向后兼 ...
随机推荐
- modelsim 独立仿真vivado的IP核及仿真脚本
Modelsim独立仿真vivado的IP 最近一直在做local dimming项目的FPGA硬件实现,算法的其中一步就是直方图统计,即数字图像的某一灰度级的像素数,这个直方图的源码找了半天才搞到, ...
- SpringBoot 构造器注入、Setter方法注入和Field注入对比
0. 引入 今天在看项目代码的时候发现在依赖注入的时候使用了构造器注入,之前使用过 Field 注入和 Setter 方法注入,对构造器注入不是很了解.经过查阅资料看到,Spring 推荐使用构造器注 ...
- 八大排序算法~简单选择排序【记录下标k变量的作用】
八大排序算法~简单选择排序[记录下标k变量的作用] 1,思想:打擂台法,数组中的前n-1个元素依次上擂台"装嫩",后边的元素一个挨着一个不服,一个一个上去换掉它 2,优化:通过记录 ...
- 每天五分钟Go - 闭包
闭包的示例代码 func getSequence() func() int{ i:=0 return func() int { i+=1 return i } } 首先,函数名getSequence, ...
- 自建CA实现HTTPS
说明:这里是Linux服务综合搭建文章的一部分,本文可以作为自建CA搭建https网站的参考. 注意:这里所有的标题都是根据主要的文章(Linux基础服务搭建综合)的顺序来做的. 如果需要查看相关软件 ...
- 【用例】编写App测试用例的关注点
编写App测试用例的关注点 如何做到测试用例的百分百覆盖一直是测试用例编写过程中的难点,首先在测试时我们经常会遇见一些常见的bug,那么我们可以在编写测试用例时考虑到这些点. 一:关于业务逻辑 ...
- vscode配置java+gradle开发环境
1.安装扩展包Java Extension Pack,里面包含java开发所必须的扩展 2.安装java jdk,8版本就是1.8版本,根据需要安装不同的版本 3.下载gradle,将bin文件夹添加 ...
- Jmeter RMI 反序列化命令执行漏洞(CVE-2018-1297)
下载ysoserial,git git clone https://github.com/frohoff/ysoserial.git cd ysoserialmvn clean package -Ds ...
- Docker与k8s的恩怨情仇(七)—— “服务发现”大法让你的内外交互原地起飞
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 第一章:Docker与k8s的恩怨情仇(一)-成为PaaS前浪的Cloud Foundry 第二章:Dock ...
- Tensorflow2对GPU内存的分配策略
一.问题源起 从以下的异常堆栈可以看到是BLAS程序集初始化失败,可以看到是执行MatMul的时候发生的异常,基本可以断定可能数据集太大导致memory不够用了. 2021-08-10 16:38:0 ...