Java代码运行的方式

1:在开发工具中运行
2:双击 jar 文件运行
3:在命令行中运行
4:在网页中运行

上述运行方式都离不开 JRE,也就是 Java 运行时环境。实际上 JRE 仅包含运行 Java 程序的必须组件,包括 Java 虚拟机以及 Java 核心类库等。Java 程序员经常接触到的 JDK 同样包含了 JRE,并且还附带了一系列开发和诊断工具。

为什么 Java 要在虚拟机里运行

Java 是一门高级程序语言,语法复杂,抽象程度高,因此直接在硬件上运行并不现实。所以,在 Java 程序运行之前,需要对其进行转换。

设计一个面向 Java 语言的特性的虚拟机,并通过编译器将 Java 程序转换成该虚拟机所能识别的指令程序,也叫 Java 字节码。之所以叫做字节码,是因为 Java 字节码指令的操作码被固定为一个字节。

使用虚拟机的好处

Java 虚拟机也可以由硬件直接实现,但是更为常见的是基于软件实现。

如果一个 Java 程序被转换成 Java 字节码,那么他便可以在不同平台上的 Java 虚拟机实现里运行。这就是通常所说的:一次编译,到处运行。

虚拟机的另一个好处就是它提供了一个托管环境。这个托管环境代替我们处理一些代码中冗长而且容易出错的代码。最明显的使用就是自动内存管理和垃圾回收。托管环境还提供了诸如数组越界,动态类型,安全权限等等动态监测。

Java 虚拟机如何运行 Java 字节码

下面以标准 JDK 中的 HotSpot 虚拟机为例,从虚拟机以及底层硬件两个角度进行分析。

从虚拟机的视角来看,首先将 Java 代码编译成 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被存放于方法区,实际运行时,虚拟机会执行方法区的代码。

Java 虚拟机在内存中划分出堆和栈来存储运行时的数据。对于栈, Java 虚拟机会把其细分为面向 Java 方法的 Java 方法栈,面向本地方法(用 C++ 写的 native 方法)的本地方法栈,以及存放各个线程执行位置的 PC 寄存器。

在虚拟机运行过程中,每当调用进入一个 Java 方法, Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。

当退出当前执行的方法时,不管是正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,将之舍弃。

从硬件的视角来看,Java 字节码无法直接执行,因此 Java 虚拟机需要将字节码翻译成机器码。

翻译的形式有两种,一种是解释执行,即逐条将字节码翻译成机器码执行;一种是即时编译,即将一个方法中包含的所有字节码编译成机器码后再执行。

解释执行的优势在于无需等待编译,及时编译的优势在于实际运行速度会更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。

Java 虚拟机的运行效率

HotSpot 采用了多种技术来提升启动性能以及峰值性能,即时编译就是其中最重要的技术之一。

为了满足不同用户场景的需要,HotSpot 内置了多个即时编译器:C1,C2 和 Graal。Graal 是 Java 10 正式引入的实验性即时编译器,后续再总结。引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。

C1 又叫做 Client 编译器,面向的是对启动性能有要求的客户端 GUI 程序,采用的优化手段相对简单,因此编译时间较短。

C2 又叫做 Server 编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但生成代码的执行效率较高。

从 Java 7 开始,HotSpot 默认次用分层编译的方式:热点方法首先会被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。

为了不干扰应用正常的运行,HotSpot 的即时编译是放在额外的编译线程中进行的。HotSpot 会根据 CPU 的数量设置编译线程的数目,并且按 1:2 的比例分配给 C1 和 C2 编译器。

问答

Q:对于发布一次就长时间运行的程序,为什么不选择直接将 Java 字节码编译成机器码

事实上 JVM 却是有考虑做 AOT 的这种事情。AOT 能够在线下将 Java 字节码编译成机器码,主要用来解决启动性能不好的问题。其实线下编译和即时编译都一样,至多一两个小时后该编译的都已经编译完成了。另外,即时编译器因为有程序运行时信息,优化效果更好,也就是说峰值性能更好。

Q:如何区分热点代码和非热点代码

关于热点代码的统计有两种算法:一种是基于采样的热点探测,一种是基于计数器的热点探测。基于计数器的热点探测又有两个计数器:一种是方法调用计数器,一种是回边计数器,他们在 C1 和 C2 中有不同的阈值。默认的分层编译应该是达到两千调用 C1,达到一万五调用 C2。一般采用的都是基于计数器的热点探测。

Q:对于 JVM 的及时编译,当方法体中有很多 if,else if 这样的判断,如何编译

JVM 有两种编译方式,一种是对整个方法进行编译,一种是对热循环进行编译。无论哪种,都要比 if else 的粒度大。

总结

本文创作灵感来源于 极客时间 郑雨迪老师的《深入拆解 Java 虚拟机》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。

关注本人公众号,第一时间获取最新文章发布,每日更新一篇技术文章。

01 Java 代码是怎么运行的的更多相关文章

  1. 听说你还不知道Java代码是怎么运行的?

    作为一名Java程序员,我们需要知道Java代码是怎么运行的.最近复习了深入理解Java虚拟机这本书,做了一下笔记,希望对大家有帮助,如果有不正确的地方,欢迎提出,感激不尽. java 代码运行主要流 ...

  2. JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇

    JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇 作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题).我们也知道我们Java程序 ...

  3. 《深入理解Java虚拟机》-Java代码是如何运行的

    问题一:Java与C++区别 1.Java需要运行时环境,包括Java虚拟机以及Java核心类库等. 2.C++无需额外的运行时,通常编译后的代码可以让机器直接读取,即机器码 问题一:Java为什么要 ...

  4. Java代码是怎么运行的

    前言.... 作为一名 Java 程序员,你应该知道,Java 代码有很多种不同的运行方式.比如说可以在开发工具中运行,可以双击执行 jar 文件运行,也可以在命令行中运行,甚至可以在网页中运行.当然 ...

  5. Java代码在本地运行没有问题。上传到阿里云服务器后。出现了中文乱码解决

    java -Dfile.encoding=UTF-8 -jar project.jar

  6. MVC框架的理解(配置文件一次编写,所有的java代码都可以运行)

  7. JAVA_HOME path classpath 以及cmd编译运行java代码

    JAVA_HOME PATH CLASSPATH 三者的区别:安装完jdk之后,首先在环境变量里面添加JAVA_HOME ,例如安装路径为C:\Program Files\Java\jdk1.6.0_ ...

  8. 用DOS命令来运行Java代码

    用DOS命令来运行Java代码.. ----------------- Demo.java public class Demo { public static void main(String[] a ...

  9. Java基础之用记事本编辑java代码运行,并且打成jar包后运行

    使用记事本写java代码 1.在d盘新建一个记事本,名字叫做zhanzhuang.java,会询问不可用,是否继续,点击是 2.在里面编辑就如下内容,注意文件的名字要和 class 后面的名字相对应 ...

随机推荐

  1. 前端APP打包管理规范

    1.包命名规范1)说明打包人:姓名拼音首字母小写 dev:开发环境 test:测试环境 pre:预发布环境 prod:正式环境 例如:版本号:1.2.3,说明:第一个数字大版本:变更框架.调整结构时变 ...

  2. vmware 虚机NAT模式,局域网可访问

    本地VMware虚拟机,网络模式为NAT,现在需要局域网其他电脑通过ssh连接这台VMware虚拟机 宿主机地址:192.168.3.26 VMware虚拟机地址:192.168.239.137 局域 ...

  3. [Git]使用Git上传本地项目,并同步到Github上

    第一步:先要在github.com中创建一个仓库(New Repository). 第二步,打开Git Bash ① git init [+仓库名]:初始化仓库,执行之后可以在指定的仓库存放地上面看到 ...

  4. Android(java)学习笔记112:Activity中的onCreate()方法分析

    1.onCreate( )方法是android应用程序中最常见的方法之一: 翻译过来就是说,onCreate()函数是在activity初始化的时候调用的,通常情况下,我们需要在onCreate()中 ...

  5. HTML5中的Web Workers

    https://www.cnblogs.com/yanan-boke/p/6954390.html https://segmentfault.com/a/1190000014938305 HTML5 ...

  6. 2018.4.27 Java的Swing常用事件

    Swing常用事件 1.Java Swing中处理事件的一般步骤是: (1)新建一个组件(JButton) (2)将该组件添加到面板中(JPanel) (3)注册监听器以监听事件源产生的事件(如Act ...

  7. linux配置邮件客户端

    linux配置邮件客户端 1. 申请一个163邮箱,并配置客户端授权密码 l 开启POP3/SMTP/IMAP l 设置客户端授权密码 ###此密码不能跟邮箱密码相同,此密码用来在linux邮件客户端 ...

  8. 使用js将div高度设置为100%

      在开发的工程中使用到了一些开源的bootstrap模板进行开发,在遇到一些需要替换的内容部分部分时,经常出现高度设置100%无法生效的问题,这里来用js强行设置一下.   思路:js监听窗口的缩放 ...

  9. 微信小程序传值取值的几种方法

    一,列表index下的取值 实现方式是:data-index="{{index}}"挖坑及e.currentTarget.dataset.index来填坑即可 1.1生成值 < ...

  10. Linux下同进程多进程号实时监控

    一.需求: Linux上对一个进程名称可能会对应的多个进程号的进程进行监控,如果有多个则输出到一个日志文件. 以上问题针对的是一个定时程序还未运行结束,到下一个时刻程序又运行起来了,避免造成重复调用接 ...