JVM编译器的编译过程

一、编译器概述

本片文章叙述的是HotSpot虚拟机的编译过程,HotSpot包含一个解释器javac和两个即时编译器,它们之间配合工作。


解释器与编译器:
1、解释器与编译器两者各有优势,当程序需要快速启动和执行的时候,解释器可以后先发挥作用,省去编译时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获得更高的执行效率。
2、当内存资源限制较大的时候,可以使用解释器执行节约内存,反之,可以使用编译器执行提升效率。
3、解释器可以作为编译器激进优化的一个逃生门,当激进优化不成立的时候可以通过逆优化回到解释器状态继续执行。

二、Javac的执行过程


三、即时编译器的执行过程

Java虚拟机中内置了两个即时编译器,分别为Client和Server或则叫C1和C2.
C1编译器将字节码编译为本地代码,进行简单可靠的优化,C2编译器则会启动一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。C1和C2都是编译一些热点代码(多次调用的方法或者多次执行的循环体),因此在编译前,首先要进行热点探测,HotSpot虚拟机中使用的是基于计数器的热点探测方法。
它为每个方法都准备了两个计数器,方法调用计数器和回边计数器。
方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间内方法被调用的次数。当超过一定的时间限度,如果方法调用次数仍然不足以让它提交给即时编译器,那这个方法的调用计数器就会衰减一般,这个过程称为方法调用计数器热度的衰减。
回边计数器统计的是一个方法中循环体代码执行的次数,它没有热度衰减。建立回边计数器统计的目的是为了处罚OSR编译,OSR即栈上替换,也就是编译发生在方法执行过程之中。

1、方法调用计数器触发即时编译


2、回边计数器触发OSR编译


四、即时编译器的编译过程

1、C1编译过程

对于C1来说,它是一个简单快速的三段式编译器,主要关注点在于局部性的优化,而放弃了许多耗时较长的全局优化。
1、在字节码上进行一些基础优化,方法内联、常量传播。
2、将字节码构造成高级中间表示HIR(静态单分配),再次进行一些优化,空值检查消除、范围检查消除等。
3、将HIR转换成LIR,寄存器分配、窥孔优化、机器码生成


2、C2编译过程

C2会执行所有的经典优化动作,如无用代码删除、循环展开、循环表达式外提、消除公共子表达式、常量传播、基本快重排序等。

五、Java与C/C++编译器对比

1、即时编译器占用的是用户程序的运行时间,具有很大压力,使得编译器不敢引入大规模的优化技术。
2、Java是动态的语言类型,意味着需要由虚拟机来确保程序不会违反语言语义或访问非结构化内存。这就意味着虚拟机需要进行频繁的动态类型检查。
3、Java语言中虽然没有virtual关键字,但是使用虚方法的频率要远远大于C/C++。这就意味着运行时对方法接受者进行多态安泽的频率要远远大于C、C++。
4、Java语言是可以动态扩展的语言,运行时加载新的类可能改变程序类型的继承关系,这使得很多全局的优化都难以进行,因为编译器无法无法看见程序的全貌。
5、Java语言中对象的内存都是在堆上分配,只有方法中的局部变量才能在栈上分配,需要垃圾回收。

但Java编译器也有自己的优点,由于C++编译器所有的优化都是在编译器完成,以运行期性能监控为基础的优化措施它都无法进行,如调用平率预测,分支频率预测裁剪。
发布了10 篇原创文章 · 获赞 18 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览