欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java为什么是半编译半解释型语言

程序员文章站 2023-03-26 14:37:14
1、首先让我们先弄清楚什么是编译型语言,什么是解释型语言?编译型语言:编写好程序以后,首先需要编译器进行编译,统一转化成机器码,然后这个编译完的文件,可以放在操作系统直接执行解释型语言: 程序是边运行边进行机器码转化(转化完后cpu执)引用“李白写代码”(【这就好像我们吃一袋瓜子,解释性语言是剥一颗吃一颗,直到吃完;而编译性语言是先把一袋瓜子全部剥完,剥出肉,然后一口吃进去。你说这两种模式对于最后吃的人来说,哪种会比较快,结果不言而知吧!】这里的一袋瓜子其实就是一段程序,吃瓜子的人就是操作系统。)...

1、首先让我们先弄清楚什么是编译型语言,什么是解释型语言?

  • 编译型语言:编写好程序以后,首先需要编译器进行编译,统一转化成机器码,然后这个编译完的文件,可以放在操作系统直接执行
  • 解释型语言: 程序是边运行边进行机器码转化(转化完后cpu执)

引用“李白写代码”(【这就好像我们吃一袋瓜子,解释性语言是剥一颗吃一颗,直到吃完;而编译性语言是先把一袋瓜子全部剥完,剥出肉,然后一口吃进去。你说这两种模式对于最后吃的人来说,哪种会比较快,结果不言而知吧!】这里的一袋瓜子其实就是一段程序,吃瓜子的人就是操作系统。)
2、什么是模板解释器、字节码解释器(前提都是直接读取的.class文件)

  • 字节码解释器:读取字节码,转换为c++代码,再由c++代码转换为硬编码,以此类推。其字节码解释器主要的缺点是执行比较慢(Java字节码->c++代码->硬编码);
  • 模板解释器:刚开始的运行原理和字节码解释器一样,只不过模板解释器里面有个特殊的机制-即时编译(即时编译底层原理:1、申请一块内存:可读可写可执行
    2、将处理new字节码的硬编码拿过来
    3、将处理new字节码的硬编码写入申请的内存
    4、申请一个函数指针,用这个函数指针执行这块内存
    5、调用的时候,直接通过这个函数指针调用就可以了)
    (Java字节码->硬编码)

3、模板解释器和字节码解释器的区别及其应用场景

  • 由于模板解释器已经提前生成了字节码到硬编码的映射,当读取到一条字节码,只需要去一张映射表中,找到当前字节码对应的机器指令,这个时候,直接执行机器码。而字节码解释器,是读取到一条字节码,然后把其翻译成C++代码,然后再由C++代码生成机器指令,这样的执行过程较模板解释器来说,执行效率就没那么高。
    补充:上面所说的模板解释器提前生成的字节码到硬编码的过程,其实是在程序启动的时候,生成的,如果当前项目较大的话,启动程序的时候,一定需要很长的时间,因为在启动的时候需要生成字节码到硬编码的映射,当然这样做的目的是,执行效率会更高。

4、JVM的三种运行模式

  • -Xint 纯字节码解释器(1)
  • -Xcomp 纯模板解释器(2)
  • -Xmixed 字节码解释器 + 模板解释器(3)
    注:JVM默认是混合模式,我们可以执行下java -version查看下,可以通过java -Xint version来设置JVM的运行模式为纯字节码解释器。上面三个中执行模式中性能排比是什么呢,321或者是231,直接影响2和3的性能因素是,程序的大小。如果是大程序的话,可以直接采用混合模式,启动时间较快,编译优化器可以根据热点代码等进行优化。
    java为什么是半编译半解释型语言

基于上面的模板解释器运行模式,我们在现实生活中会遇到这样的问题,比如,在用解释器解释字节码时,有时候会出现,相同的代码会被执行多次,这样的话,就会导致,花费了很多时间,浪费了很多资源,当然,既然出现问题了,那就有相对应的解决方案,即时编译器诞生了,即时编译是一个动态编译的过程,即时编译器其实就是监测解释器执行的代码块,它会把代码块对应的执行次数记录下来,当执行次数很多的时候,就会优化对应的热点代码,并把对应的机器指令更新在映射表中,这样在下次执行到类似的代码块时,就可以直接运行更新了的的机器指令。接下来让我们再来看下即时编译器吧 ^ _ ^

5、即时编译器(JIT,即时编译器生成的代码就是给模板解释器用的)
HotSpot虚拟机内置了两个即时编译器,分别称为Client Compiler和Server Compiler,习惯上将前者称为C1,后者称为C2。

  • c1
    1、触发的条件相对C2比较宽松:需要收集的数据较少
    2、编译的优化比较浅:基本运算在编译的时候运算掉了
String s1 = "1"; String s2 = "2"; final String s3 = s1 + s2;//按理来说这一步应该会产生一个新的String对象,但是编译器判断是final类型,直接会将s1+s2替换为“12” 
 3、c1编译器编译生成的代码执行效率较C2低 
  • c2
    1、触发的条件比较严格,一般来说,程序运行了一段时间以后才会触发
    2、优化比较深(优化汇编指令)
    3、编译生成的代码执行效率较C1更高
  • 混合编译
    程序运行初期触发C1编译器
    程序运行一段时间后触发C2编译器
    Client 编译器模式下,N 默认的值 1500(N表示热点代码的次数)
    Server 编译器模式下,N 默认的值则是 10000

6、即时编译触发的条件:热点代码(存放在方法区)
在程序运行期间,根据对热点字节码的探测(运行次数超过某个阀值的代码),将这部分热点代码进行特别的优化,将其直接编译为本地机器码执行并缓存。其使用的定期清理算法是LRU,最近最久未使用算法

java -client -XX:+PrintFlagsFinal -version | grep CICompilerCoun //查看当前执行即时编译的线程个数,intx CICompilerCount表示个数 

7、即时编译器是如何运行的呢?

  • 将即时编译任务(即函数弹出栈的次数)写入一个队列中;
  • VM_THREAD 读取任务,并运行
    注:所以即时编译是一个异步的操作

8、即时编译的优化方式

  • 逃逸分析:什么叫逃逸呢?逃逸就是这个对象的作用域不是局部的,逃逸的对象优化工作很困难。

9、基于逃逸分析,JVM开发了三种优化技术

  • 栈上分配:逃逸分析如果是开启的,栈上分配就是存在的(不发生gc的情况下,查看堆上的对象个数,如果是程序中创建的个数,就存在栈上分配)
  • 标量替换:标量:不可再分,java中的基本类型就是标量
import lombok.Data; public class ScalarSubstitution { public static void main(String[] ars) { Point point = new Point(); System.out.println(point.x); //编译器会替换成System.out.println(0); System.out.println(point.y); //同上 } } @Data class Point { public int x; public int y; } 
  • 锁消除:
public void test(){ synchronized (new Object()){ //编译器判定这个对象是个局部变量,是线程私有的,所以就没必要加锁,会直接把锁去掉 System.out.println("zong"); } } 

10、了解了上面的这些知识点以后,对于如果别人问你什么是半编译半解释型语言,知道该怎么回答了么?
编译指的是javac编译生成的字节码文件.class,但为什么是半呢,是因为生成的这个.class文件操作系统不能直接执行,需要解释器进行解释后,才可能运行,所以才把java叫做半编译半解释型语言。

本文地址:https://blog.csdn.net/Zong__Zong/article/details/108020997