最近因为公司的一些原因,我也开始学习一些 JAVA 的知识。虽然我一直是以 .NET 语言为主的程序员,但是我并不排斥任何其它语言。在此并不讨论 JAVA .NET 的好坏,仅仅是对 .NET 跟 JAVA 程序的编译执行过程进行一些简单的介绍跟比较。因为有些内容还是超出自己原来的认知的,所以整理一下做个记录。

.NET

.NET 程序的执行过程大概分以下几个步骤:

  1. 代码
  2. 语言编译器编译
  3. IL
  4. JIT 编译
  5. 运行

.NET 平台的程序编译的时候是分多步的。当我们写好代码开始编译的时候需要选择一个合适的编译器比如csc 、vbc 。经过这一次编译之后我们的程序会被打包成 dll

或者 .exe 文件。这些 dll 里面其实包含的是 MSIL 。IL 做为一种中间语言,为跨平台提供了基础。当我们把这些文件复制到目标机器上需要真正运行的时候,JIT (just-in-time compilation)编译开始工作了。CLR 为我们在每个支持的平台上都实现了一个 JIT 编译器,当一个方法在第一次运行的时候,JIT 编译会把 IL 编译成目标机器的机器码,这样我们的程序才能真正运行。这也是为什么 .NET 程序第一次运行的时候会慢一点的原因。解决这个问题我们可以使用工具 Ngen.exe 在第一次运行前进行一次预编译,这样就可以提升 .NET 程序的启动速度。

分层编译

上面大概描述了 .NET 程序编译过程。但是 JIT 编译可能还有一些特性需要讲一下,比如分层编译。

分层编译是从 .NET core 2.1 开始引入的一个特性。我们的 IL 到机器码,需要 JIT 进行一次编译,这会影响 .NET 程序的第一次运行的速度。微软为了解决这个问题引入了分层编译。分层编译把 JIT 编译分成两次。当一个方法第一次被执行的时候,JIT 编译器会进行第一次快速编译,这次编译并不会进行特别的优化操作,追求的是编译的速度。当我们的程序运行一段时间后,CLR 会自动感知到频繁运行的代码,这些代码被称为热点代码。当出现热点代码的时候 JIT 编译器会重新进行一次优化编译来提高热点代码的执行效率,从而提高整个程序的性能。

通过 JIT 分层编译, .NET 程序很好的在编译速度跟性能之间找到了平衡。

JAVA

JAVA 程序的执行过程大概分以下几个步骤:

  1. 代码
  2. 语言编译器编译
  3. 字节码
  4. 解释/JIT编译
  5. 运行

下面说说 JAVA 程序的编译过程。

当我们编写好 JAVA 程序,想要执行的时候,跟 .NET 程序一样,同样会选择一个语言编译器来进行第一次编译。因为 JVM 语言有好多种,比如 JAVA ,kotlin ,所以同样会有多种语言编译器,比如 javac,kotlinc 等等。这里还是以标准的 JAVA 为例,在语言编译器编译完源代码后,会生成一堆 .class 的文件,这些文件包含的内容被称之为字节码。字节码的存在跟 MSIL 类似,同样为跨平台提供了一种很好的方案。只要为每个平台实现接口一致的 JVM , 让这些 JVM 来运行字节码就可以跨平台了。

解释执行

当我们真正要执行 JAVA 程序的时候,这些字节码会被 JVM 执行。JVM 执行的时候首先会在 CodeCache 内查找这个方法有没有编译好的机器代码,如果没有那么交给“解释执行器”来解释执行。所谓解释执行,就是将代码一行行的经过解释器进行翻译成机器码后让目标机器执行。但是这些翻译的产物并不会被记录下来,也就是说同样的代码每次执行的时候都需要解释器进行翻译。

JIT 编译

显然对于一些重复执行的方法解释器执行效率会很低。为了解决这个问题,设计 JVM 的工程师们想出了办法。以 Hotspot 为例,当程序经过一段时间的解释执行后,JVM 会记录这些方法的执行次数,当一些方法反复被执行的时候,JVM 会认为这些方法是热点代码。这时候 JVM 会对这些热点代码进行一次 JIT 编译,这次 JIT 编译还会根据运行时的 profile 进行优化。编译完成后把 JIT 编译的产物固定下来,存储在 CodeCache 中。这样当一个方法下次再次被执行的时候 JVM 会从 CodeCache 中直接读取机器码来执行。这样热点代码的执行效率就会大大的提供,这也是为啥有些 JAVA 程序需要进行预热。

总结

通过以上我们分别描述了 .NET 跟 JAVA 程序编译执行的过程。他们之间的区别在于 .NET 程序不管什么时候都是进行 JIT 编译,并且通过分层编译技术在首次执行速度跟性能之间找到了平衡。而 JAVA 虽然做为一门静态语言,但是它的代码一开始竟然是解释执行的(当然这是对 Hotspot JVM而言的,有的 JVM 未必是这样),在运行的时候才会对热点代码进行 JIT 编译优化代码。虽然大家实现的方式不同,但是殊途同归,都是通过对热点代码的二次编译实现了对程序的性能的优化。

参考

https://docs.microsoft.com/zh-cn/dotnet/standard/managed-execution-process

https://www.zhihu.com/question/37389356/answer/73820511

关注我的公众号一起玩转技术

关于 .NET 与 JAVA 在 JIT 编译上的一些差异的更多相关文章

  1. 你的java 代码对JIT编译友好吗?

    JIT编译器是Java虚拟机(以下简称JVM)中效率最高并且最重要的组成部分之一.但是很多的程序并没有充分利用JIT的高性能优化能力,很多开发者甚至也并不清楚他们的程序有效利用JIT的程度. 在本文中 ...

  2. 你的Java代码对JIT编译友好么?(转)

    JIT编译器是Java虚拟机(以下简称JVM)中效率最高并且最重要的组成部分之一.但是很多的程序并没有充分利用JIT的高性能优化能力,很多开发者甚至也并不清楚他们的程序有效利用JIT的程度. 在本文中 ...

  3. 【深入Java虚拟机】之七:Javac编译与JIT编译

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...

  4. Java虚拟机 - Javac编译与JIT编译

    [深入Java虚拟机]之七:Javac编译与JIT编译 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤 ...

  5. 深入理解java虚拟机(十三) Java 即时编译器JIT机制以及编译优化

    在部分的商用虚拟机中,Java 程序最初是通过解释器( Interpreter )进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”.为了提高热点代码的 ...

  6. Java 面试-即时编译( JIT )

    当我们在写代码时,一个方法内部的行数自然是越少越好,这样逻辑清晰.方便阅读,其实好处远不止如此,通过即时编译,甚至可以提高执行时的性能,今天就让我们好好来了解一下其中的原理. 简介 当 JVM 的初始 ...

  7. 90% 的 Java 程序员都说不上来的为何 Java 代码越执行越快(1)- JIT编译优化

    麻烦大家帮我投一票哈,谢谢 经常听到 Java 性能不如 C/C++ 的言论,也经常听说 Java 程序需要预热,那么其中主要原因是啥呢? 面试的时候谈到 JVM,也有很多面试官喜欢问,为啥 Java ...

  8. 【Java】实战Java虚拟机之五“开启JIT编译”

    今天开始实战Java虚拟机之五“开启JIT编译” 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Ja ...

  9. javac 编译与 JIT 编译

    编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行: 其中绿色的模块可以选择性实现.很容易看出,上图中 ...

随机推荐

  1. 阿里云视频云 Retina 多媒体 AI 体验馆开张啦!

    带你体验视频更多可能 海量视频管理难度大?翻库检索特定人物费时费力?视频内容剪辑效率低?您的得力助手"Retina多媒体AI"体验馆已上线.带你感受视频AI黑科技,开启极致智能体验 ...

  2. 提高GUI自动化测试稳定性解决方案

    针对"GUI自动化测试稳定性问题"这个问题,最典型的情景就是:同样的测试用例,在同样的测试执行环境下,测试的结果有时是Success,有时是Fail,这严重降低了GUI测试的可信度 ...

  3. 基于Android平台的图书管理系统的制作(3)

    前两篇介绍了主页面和Student,这一篇来讲Book类和工作人员. Book是图书管理系统的核心,查书,借书,还书,增加书都与Book类息息相关.Book类的设计很简单:包含信息:名称.作者.页数. ...

  4. Java线程的并发工具类

    Java线程的并发工具类. 一.fork/join 1. Fork-Join原理 在必要的情况下,将一个大任务,拆分(fork)成若干个小任务,然后再将一个个小任务的结果进行汇总(join). 适用场 ...

  5. 【NX二次开发】创建老版的基准平面uf5374

    使用uf5374() 源码: double dP1[3] = { 0.0,0.0,0.0 }; double dP2[3] = { 0.0,1.0,0.0 }; double dP3[3] = { 0 ...

  6. 你,确定了解Java的String字符串?

    本文将描述JDK6中String.intern()是如何实现的,以及在JDK7和JDK8中对字符串池化技术做了哪些改变. String池化介绍 String池化就是把一些值相同,但是标识符不同的字符串 ...

  7. 面试官:为什么Mysql中Innodb的索引结构采取B+树?

    前言 如果面试官问的是,为什么Mysql中Innodb的索引结构采取B+树?这个问题时,给自己留一条后路,不要把B树喷的一文不值.因为网上有些答案是说,B树不适合做文件存储系统的索引结构.如果按照那种 ...

  8. android悬浮窗口

    悬浮窗原理 做过悬浮窗功能的人都知道, 要想显示悬浮窗, 要有一个服务运行在后台, 通过getSystemService(Context.WINDOW_SERVICE)拿到WindowManager, ...

  9. Unity VideoPlayer视频模糊

    如果视频出现模糊或者尺寸不对只需要修改Texture的Size就可以 注意一点  Size的值要跟视频尺寸一直

  10. VMware-克隆虚拟机(CentOS7)

    采用克隆完整克隆 修改系统参数,除了IP和主机名以外. 第一步 修改mac地址 在虚拟机还未启动之前,先修改该系统的mac地址,如下操作所示. 生成新的mac地址 修改主机名(reboot后生效) $ ...