JVM基本概念,Java编译方式,Javac编译的过程

JDK、JRE、JVM之间的关系

请添加图片描述

  1. JVM是Java 程序的运行环境它包括class类加载器、JIT动态编译器、执行引擎、以及垃圾收集器等,它可以将java程序生成的字节码文件解释成具体系统平台上的机器指令,然后将这些机器指令映射到CPU指令集或OS的系统中,从而让具体平台如window运行这些Java程序。但jvm不能直接运行字节码文件,因为jvm在将字节码文件解释成机器指令的时候需要加载程序所需要的类库。
  2. JRE是支持Java程序运行的标准环境, 它包含了JVM以及Java的核心类库,它可以运行已经编译好的字节码文件。它还包括两项关键的部署技术:Java 插件 — 使小程序可以在常用浏览器中运行;以及 Java Web Start — 通过网络部署独立的应用。它还是用于企业软件开发和部署的 Java 2 Platform, Enterprise Edition (J2EE) 的基础。
  3. JDK包括了Java运行环境JRE、一些Java工具(文档工具javadoc、打包工具jar.exe等)和更多Java类库(如tools.jar),它包含了Javac编译器,可以将java文件编译成字节码文件。

JVM虚拟机发展历程

JDK各版本新特性

java的编译方式

前端编译、即时编译(JIT编译)、静态提前编译(AOT编译);

前端编译

前端编译器:Oracle javac、Eclipse JDT中的增量式编译器(ECJ)等;

  1. 前端编译就是将**Java源码文件(.java)编译成Class文件(.class)**的过程,即把满足Java语言规范的程序转化为满足JVM规范所要求格式的功能;
  2. 添加默认构造器,自动拆装箱,泛型集合取值,可变参数,foreach循环,switch字符串,switch枚举,枚举类,try-with-resources,方法重写时的桥接方法,匿名内部类,泛型等都是在这一阶段进行的。
  3. 优点:
    1. 许多Java语法新特性(“语法糖”:泛型、内部类等等),是靠前端编译器实现的,而不是依赖虚拟机;
    2. 编译成的Class文件可以直接给JVM解释器解释执行,省去编译时间,加快启动速度;
  4. 缺点:
    1. 对代码运行效率几乎没有任何优化措施;
    2. 解释执行效率较低,所以需要结合下面的JIT编译;

即时编译

  1. JVM中内置JIT编译器;
  2. 字节码编译成本地机器码的过程;
  3. 优点:
    1. 通过在运行时收集监控信息,把"热点代码"编译成与本地平台相关的机器码,并进行各种层次的优化;
    2. 可以大大提高执行效率;
  4. 缺点:
    1. 收集监控信息影响程序运行;
    2. 编译过程占用程序运行时间(如使得启动速度变慢);
    3. 编译机器码占用内存;
  5. JIT编译速度及编译结果的优劣,是衡量一个JVM性能的很重要指标;
  6. 所以对程序运行性能优化集中到这个阶段;也就是说可以对这个阶段进行JVM调优;

静态提前编译(AOT编译)

静态提前编译器(AOT编译器):JAOTC、GCJ、Excelsior JET、ART (Android Runtime)等;

  1. 程序运行前,直接把 Java源码文件(.java)编译成本地机器码的过程;
  2. 它可以解决java冷启动问题(冷启动问题是指Java应用并不是即起即用的,而需要经过虚拟机初始化后才能达到可用状态,再经过程序预热(JIT在出现热点函数才开始)才能达到最佳性能);
  3. 优点:
    1. 编译不占用运行时间,可以做一些较耗时的优化,并可加快程序启动;
    2. 把编译的本地机器码保存磁盘,不占用内存,并可多次使用;
  4. 缺点:
    1. 因为Java语言的动态性如反射(反射的原理是JVM根据class文件的内容来做一些操作,静态编译之后,代码就不存在class文件的结构了)带来了额外的复杂性,影响了静态编译代码的质量;

GraalVM的静态编译

  1. 出现原因:
    1. Java冷启动问题,它的根本原因是,Java的虚拟机模型机制和从解释执行到JIT执行的分层次执行模型;启动一个Java程序并让它达到性能的峰值需要经过:VM初始化→应用程序初始化→字节码解释执行→JIT编译热点函数→执行JIT编译后的本地代码(native code)等环节,冷启动处理步骤多,如果只是在各个环节上进行优化,很难达到理想的效率;
    2. 因为云原生的兴起,很多应用被部署再云服务器环境中,以计算资源的形式为用户提供服务。在这种趋势下,应用本身越来越小,对跨平台的需求越来越弱(因为平台问题已经由云厂商解决了),但是对应用快速启动、即起即用和高性能执行的需求越来越强。所以在这种环境下,Java程序的跨平台能力和支持与平台无关的Java字节码可以在不同的操作系统中运行(解释执行、JIT执行等问题都是由此衍生而来的),这两个由jvm提供的作用显得不那么重要了。
  2. GraalVM的静态编译的基本原则是封闭性假设(closed world assumption),要求编译器在编译时必须掌握运行时所需的全部信息,换句话说,就是运行时不能出现任何编译时未知的内容。这是因为应用程序的可达范围在静态编译时被限定了,因为没有了类加载器、解释器等组件,不能在运行时解析和执行任何动态引入的类。
  3. 与传统Java运行模型相比,GraalVM的静态编译运行模型有两大特点
    1. 静态编译后的可执行程序已经是本地程序,而且自包含了轻量级运行时支持,因此不再额外需要Java虚拟机。没有了JVM,自然也就使得应用程序达到即起即用的状态。另外,因为JVM的运行也需要消耗一部分内存,去掉JVM后应用程序的内存占用也大幅降低。
    2. 静态编译后的程序也经过了众多的编译优化,运行时不再需要经过解释执行和JIT编译,既避免了解释执行的低效,也避免了JIT编译的CPU开销,还解决了传统Java执行模型中无法充分预热,始终存在解释执行的问题,因此可以保证应用程序始终以稳定的性能执行,不会出现性能波动。

前端编译+JIT编译

  1. Java体系中主要采用的编译方式,JDK中的HotSpot虚拟机也是采用这种方式。
  2. 运作过程大体如下:
    1. 首先通过前端编译把符合Java语言规范的程序代码转化为满足JVM规范所要求Class格式;
    2. 然后程序启动时Class格式文件发挥作用,解释执行,省去编译时间,加快启动速度;
    3. 针对Class解释执行效率低的问题,在运行中收集性能监控信息,得知"热点代码"(热点探测技术);
    4. JIT逐渐发挥作用,把越来越多的热点代码"编译优化成本地代码,提高执行效率;

前端编译的过程

javac编译器

  1. javac编译器是官方JDK中提供的前端编译器,JDK/bin目录下的javac只是一个与平台相关的调用入口,具体实现在JDK/lib目录下的tools.jar。
  2. avac是由Java语言编写的,而HotSpot虚拟机则是由C++语言编写;标准JDK中并没有提供javac的源码,而在OpenJDK中的提供;javac编译器源码下载(JDK8)javac编译器源码目录:* *\src\share\classes\com\sun\tools\javac;javac编译器程序入口:com.sun.tools.javac.Main类中的main()方法;

javac的用法和解析命令行参数

用法: javac <options> <source files>
其中, 可能的选项包括:
  @<filename>                  从文件读取选项和文件名
  -Akey[=value]                传递给注释处理程序的选项
  --add-modules <模块>(,<模块>)*
        除了初始模块之外要解析的根模块; 如果 <module>
                为 ALL-MODULE-PATH, 则为模块路径中的所有模块。
  --boot-class-path <path>, -bootclasspath <path>
        覆盖引导类文件的位置
  --class-path <path>, -classpath <path>, -cp <path>
        指定查找用户类文件和注释处理程序的位置
  -d <directory>               指定放置生成的类文件的位置
  -deprecation                 输出使用已过时的 API 的源位置
  --enable-preview             启用预览语言功能。要与 -source 或 --release 一起使用。
  -encoding <encoding>         指定源文件使用的字符编码
  -endorseddirs <dirs>         覆盖签名的标准路径的位置
  -extdirs <dirs>              覆盖所安装扩展的位置
  -g                           生成所有调试信息
  -g:{lines,vars,source}       只生成某些调试信息
  -g:none                      不生成任何调试信息
  -h <directory>               指定放置生成的本机标头文件的位置
  --help, -help, -?            输出此帮助消息
  --help-extra, -X             输出额外选项的帮助
  -implicit:{none,class}       指定是否为隐式引用文件生成类文件
  -J<flag>                     直接将 <标记> 传递给运行时系统
  --limit-modules <模块>(,<模块>)*
        限制可观察模块的领域
  --module <模块>(,<模块>)*, -m <模块>(,<模块>)*
        只编译指定的模块,请检查时间戳
  --module-path <path>, -p <path>
        指定查找应用程序模块的位置
  --module-source-path <module-source-path>
        指定查找多个模块的输入源文件的位置
  --module-version <版本>        指定正在编译的模块版本
  -nowarn                      不生成任何警告
  -parameters                  生成元数据以用于方法参数的反射
  -proc:{none,only}            控制是否执行注释处理和/或编译。
  -processor <class1>[,<class2>,<class3>...]
        要运行的注释处理程序的名称; 绕过默认的搜索进程
  --processor-module-path <path>
        指定查找注释处理程序的模块路径
  --processor-path <path>, -processorpath <path>
        指定查找注释处理程序的位置
  -profile <profile>           请确保使用的 API 在指定的配置文件中可用
  --release <release>
        为指定的 Java SE 发行版编译。支持的发行版:7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
  -s <directory>               指定放置生成的源文件的位置
  --source <release>, -source <release>
        提供与指定的 Java SE 发行版的源兼容性。支持的发行版:7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
  --source-path <path>, -sourcepath <path>
        指定查找输入源文件的位置
  --system <jdk>|none          覆盖系统模块位置
  --target <release>, -target <release>
        生成适合指定的 Java SE 发行版的类文件。支持的发行版:7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
  --upgrade-module-path <path>
        覆盖可升级模块位置
  -verbose                     输出有关编译器正在执行的操作的消息
  --version, -version          版本信息
  -Werror                      出现警告时终止编译

  1. 解析命令行参数,由com.sun.tools.javac.main.Main.compile()方法处理

在这里插入图片描述

javac编译过程

  1. JVM规范定义了Class文件结构格式,但没有定义如何从java程序文件转化为Class文件,所以不同编译器可以有不同实现。
  2. 它可以分为3个子过程:
    1. 解析与填充符号表过程:解析主要包括词法分析和语法分析两个过程;
    2. 插入式注解处理器的注解处理过程;
    3. 语义分析与字节码的生成过程;
  3. javac编译动作入口: com.sun.tools.javac.main.JavaCompiler类;
    1. 准备过程:初始化插入式注解处理器;
    2. 执行注解处理;
      1. 过程一:输入到符号表
      2. 过程二:词法分析和语法分析
    3. 分析以及字节码生成
      1. 过程一:标注
      2. 过程二:数据流分析
      3. 过程三:解语法糖
      4. 过程四:生成字节码
        在这里插入图片描述
        在这里插入图片描述
  4. javac中的访问者模式
    1. 访问者模式可以将数据结构和对数据结构的操作解耦,使得增加对数据结构的操作不需要修改数据结构,也不必修改原有的操作,而执行时再定义新的Visitor实现者就行了。
    2. Javac经过第一步解析(词法分析和语法分析),会生成用来一棵描述程序代码语法结构的抽象语法树,每个节点都代表程序代码中的一个语法结构,包括:包、类型、修饰符、运算符、接口、返回值、甚至注释等;而后的不同编译阶段都定义了不同的访问者去处理该语法树(节点)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值