当前位置: 首页 > 科技观察

Java比C++慢?看完测试结果,颠覆了我的认知,不得不说,JIT真的很强大

时间:2023-03-12 18:38:27 科技观察

转载本文请联系Java大厂面试官公众号。每个好人都有沉默的时候。那段时间,我努力工作,忍受着寂寞和寂寞。日后说起时,我会感动。1.概述编程语言是按照抽象层次来分类的。我们区分高级语言(Java、Python、JavaScript、C++、Go)、低级语言(汇编程序),最后是机器码。每一种高级语言代码(如Java)都需要转换为机器本机代码才能执行。翻译过程可以是编译,也可以是口译。但是,还有第三种选择。试图结合使用这两种方法。2.编译与解释让我们先看看编译型语言和解释型语言之间的一些区别。2.1编译型语言编译器将编译型语言(C++、Go)直接转换成机器码。它们在执行前需要一个明确的构建步骤。这就是为什么每次更改代码都需要重新编译程序的原因。编译型语言往往比解释型语言更快、更高效。但是,它们生成的机器代码是特定于平台的。2.2解释型语言在解释型语言(Python、JavaScript)中,没有构建步骤。相反,解释器在程序执行时对程序的源代码进行操作。曾经认为解释型语言比编译型语言慢很多。然而,随着即时编译(JIT)的发展,性能差距正在缩小。JIT编译器在程序运行时将代码从解释语言转换为机器代码。此外,我们可以在Windows、Linux或Mac等多种平台上执行解释语言代码。解释代码不依赖于特定类型的CPU体系结构。3.编写一次随处运行Java和JVM在设计时就考虑到了可移植性。因此,当今流行的大多数平台都可以运行Java代码。这听起来像是在暗示Java是一种纯粹的解释型语言。但是,Java源代码在执行前需要编译成字节码。字节码是JVM固有的一种特殊的机器语言。JVM在运行时解释并执行此代码。它是为支持Java的每个平台构建和定制的JVM,而不是我们的程序或库。JVM也有一个JIT编译器。这意味着JVM在运行时优化我们的代码以获得与编译语言类似的性能优势。4、Java编译器javac命令行工具将Java源代码编译并转换为Java类文件(xxx.class)和平台无关的字节码:$javacHelloWorld.java源代码文件有一个.java文件。代码类文件有一个.class后缀。5、Java虚拟机编译出来的class文件(字节码)可以被JVM执行:$javaHelloWorldHelloJava!如何在运行时将字节码转换为机器本机代码。5.1体系结构概述JVM由五部分组成:类加载器JVM内存结构执行引擎Native方法接口本地方法库5.2类加载器JVM使用ClassLoader将编译好的类文件加载到JVM内存中除了加载,ClassLoader还进行链接和初始化。验证字节码是否存在安全漏洞为静态变量分配内存将符号内存引用替换为原始引用为静态变量赋原始值执行所有静态代码块5.3执行引擎执行引擎负责读取字节码并将其转换为机器版本机器码并执行它。三个主要组件负责执行,包括解释器和编译器:由于JVM是平台无关的,它使用解释器来执行字节码JIT编译器在重复的方法调用中将字节码编译为本机代码以提高性能。垃圾收集器收集并删除所有未引用的对象。执行引擎利用本地方法接口(JNI)调用本地库和应用程序。5.4Just-in-TimeCompiler(JIT)解释器的主要缺点是每次调用方法时都需要解释执行,比编译好的本地代码慢。Java使用JIT编译器来克服这个问题。JIT编译器不能完全替代解释器。它仍然被执行引擎使用。但是,JVM根据调用方法的频率使用JIT编译器。JIT编译器将整个方法的字节码编译成机器本机代码,因此可以直接重用。与标准编译器一样,生成中间代码并对其进行优化,然后生成机器本机代码。探查器是JIT编译器的一个特殊组件,负责查找热点。JVM根据运行时收集的分析信息决定编译哪些代码。这样做的效果是,经过几个执行周期后,Java程序可以更快地执行其工作。一旦JVM了解了热点,它就可以创建运行速度更快的本机代码。6.性能比较让我们看看JIT编译如何提高Java的运行时性能。6.1斐波那契数列性能测试我们将使用一个简单的递归方法来计算第n个斐波那契数:privatestaticintfibonacci(intindex){if(index<=1){returnindex;}returnfibonacci(index-1)+fibonacci(index-2);}为了测量重复方法调用的性能增益,我们将运行Fibonacci方法100次:for(inti=0;i<100;i++){longstartTime=System.nanoTime();intresult=fibonacci(12);longtotalTime=System.nanoTime()-startTime;System.out.println(totalTime);}首先,我们将正常编译并执行Java代码:$javaFibonacci.java然后,我们将禁用JIT编译器执行相同的代码:$java-Djava.compiler=NONEFibonacci.java最后,我们将在C++和JavaScript中实现并运行相同的算法以进行比较。6.2性能测试结果让我们看看运行斐波那契数列测试后以纳秒为单位测量的平均性能:带有JIT编译器的Java-2726ns-没有JIT编译器的最快Java-17965ns-慢559ns%C++没有O2优化-9435ns-246%slowerC++withO2optimization–3639ns–JavaScript慢33%–22998ns–慢743%在此示例中,使用JIT编译器,Java的性能提高了500%以上。然而,JIT编译器确实需要一些运行才能运行。有趣的是,即使在启用O2优化标志的情况下编译C++,Java的性能也比C++代码高33%。当Java仍在被解释时,C++在前几次运行中表现得更好。Java的性能也优于使用Node运行的等效JavaScript代码,后者也使用JIT编译器。结果显示性能提高了700%以上。主要原因是Java的JIT编译器启动速度更快。7.想想从技术上讲,任何静态编程语言代码都可以直接编译成机器代码。任何编程代码也可以逐步解释。与许多其他现代编程语言一样,Java结合使用了编译器和解释器。目标是利用两全其美,实现高性能和独立于平台的执行。在本文中,我们重点介绍了HotSpot中的工作原理。HotSpot是Oracle默认的开源JVM实现。GraalVM也基于HotSpot,因此适用相同的原则。如今,最流行的JVM实现结合使用了解释器和JIT编译器。但是,其中一些也可以用于其他方式。8.结论Java结合使用了两种方法。我们用Java编写的源代码在构建过程中首先被编译为字节码。生成的字节码然后由JVM解释执行。但是,JVM还在运行时使用JIT编译器来提高性能。翻译于:https://www.baeldung.com/java-compiled-interpreted