Java 虚拟机(上篇)

该文章属于Java进阶部分的JVM入门,本章讲述了JVM的历史、Java源代码到机器码的过程以及 Class字节码文件的内部结构等。

了解了这篇文章,能让你深入地了解JVM知识,保证在短时间内掌握JVM!

JVM 入门教程(上篇)

开篇:为什么要学虚拟机

  1. 学习Java虚拟机能深入地理解Java这门语言
  2. 学习虚拟机是为了线上排查问题打下基础
  • 学会垃圾回收机制等,看懂Java虚拟机内存模型,看懂GC日志,解决线上的Java应用崩溃问题

第 1 讲 Java语言的前世今生

1. JDK 与 JRE

  • JRE 仅包含运行Java程序的必须组件,包括Java虚拟机以及Java核心类库等。
  • JDK 除了包含JRE外,还附带了一系列开发、诊断工具。
  • 一般来说,如果只需要运行Java程序,那么安装JRE即可。但如果要运行Java程序的开发,那么就需要安装JDK。

2. JDK 与 J2SE

  • Java SE 是Java三大技术体系的一个。
  • 在1998年,JDK1.2发布,Java技术体系被拆分为 J2SE、J2EE、J2ME三大体系。
  • J2SE称之为标准版技术体系,它包含了构成Java语言核心的类,如:数据库连接、网络编程、接口定义等。J2SE技术体系主要用于桌面应用软件的编程。
  • J2EE称之为企业版技术体系,它除了包含J2SE中的类,还包含用于开发企业级应用的类,如:Servle、JSP、EJB等。J2EE技术体系主要用于分布式的网络程序的开发,如电子商务网站等。
  • J2ME称之为嵌入式技术体系,它包含J2SE中的一部分类。J2ME技术体系主要用于消费电子产品的软件开发,如:手机、PDA、寻呼机等。

3. J2SE 与 Java SE

  • J2SE 与 Java SE ,其实它们指的是同一个东西,只不过在2006年JDK 1.6 发布时,讲J2SE、J2EE、J2ME的命名方式改为 Java SE 6、Java EE 6、Java ME 6。

4. Java语言的历史

  • 在 1991 年,James Gosling 博士发布了 Oak,这个是 Java 语言的前身。但一直到 1995 年的时候,Oak 语言才改名为 Java。
  • 1991 年,James Gosling 博士发布产品 Oak,这是 Java 语言的前身。
  • 1995 年,Oak 语言改名为 Java。
  • 1996 年,JDK1.0 发布,提供了纯解释执行的 Java 虚拟机实现:Sun Classic VM。
  • 1997 年,JDK1.1 发布,代表技术有:JDBC、JavaBeans、内部类、反射。
  • 1998 年,JDK1.2 发布,Java 技术体系被拆分为 J2SE、J2EE、J2ME 三大体系。
  • 2000 年,JDK1.3 发布,默认的 Java 虚拟机由 Sun Classic VM 改为 HotSopt。
  • 2002 年,JDK1.4 发布,Java 真正走向成熟,代表技术有:正则表达式、NIO等。
  • 2004 年,JDK1.5 发布,对语法易用性做了很大改进,新增了泛型、枚举等,代表技术有:并发包等。
  • 2006 年,JDK1.6 发布,将 J2EE/J2SE/J2ME 的命名方式改为 Java SE 6、Java EE 6、Java ME 6。
  • 2009 年,Sun 公司因为经营不善被 Oracle 公司收购。
  • 2011 年,JDK1.7 发布。
  • 2013 年,JDK1.8 发布。
  • …………

5. 总结

  • 这一部分简单地介绍了一些常见概念上地区别,以及Java语言的历史,让大家对Java语言的发展有一个大致的理解。

第 2 讲 Java虚拟机的历史

1. Sun Classic —— 虚拟机始祖

  • 在 1996 年 1 月 23 日,Sun 发布 JDK1.0,齐总自带的虚拟机就是Classic VM。但是这款虚拟机有个特点,只能使用纯解释器的方式来执行Java代码,此时解释器与编译器无法共同存在。到JDK1.4正式退出历史舞台。

2. Sun Exact VM —— 无疾而终

  • 在 JDK1.2 时发布了Exact VM 的虚拟机,尝试解决 Classic VM遇到的所有问题,它的执行系统解决了Classic VM 存在的编译器和解释器无法同时工作的问题,还具备了一些现代高性能处理器的特性,如:两级即时编译等。
  • Exact VM 还改进了虚拟机的对象查找方式,使用了准确式内存管理,即虚拟机可以知道内存中某个位置的数据具体是什么类型,这样就减少了查找的开销,提升了执行性能。
  • 但可惜的是,虽然Exact VM发布了,但是直到它退出时,都没有被大规模使用过。

3. Sun HotSpot VM —— 武林盟主

  • HotSpot不仅仅有前面说到两款虚拟机的优点,也有许多自己的新技术,如:热点探测技术。热点探测技术指的是通过执行计数器找出最具优化价值的代码,然后通知JIT编译器一方法为单位进行深度优化编译。
  • 从 2000 年JDK1.3发布,HotSpot VM作为默认的虚拟机开始登上历史舞台。

4. BEA JRockit / IBM J9 VM —— 百家争鸣

  • 前面说的都是 Sun 公司推出的虚拟机,其他组织、公司也研发过不少的虚拟机实现。这里是最著名的。
  • BEA 公司的 JRockit 是一款专注于服务器硬件和服务端应用场景的虚拟机,其针对服务端场景做了大量的优化,因此其不太关注程序启动速度。JRockit 虚拟机内部不包含解释器实现,全部代码都靠即时编译器编译后执行。此外,其提供的 MissionControl 服务套件也十分强大。
  • IBM 公司的 J9 VM 则是一款比较通用的虚拟机,其定位应用于从服务端到桌面应用再到嵌入式的多用途虚拟机。IBM 公司开发 J9 VM 的目的是将其作为 IBM 公司各种 Java 产品的执行平台。

5. 那些无名虚拟机 —— 武林外传

  • 除了上述的这些虚拟机之外,其实还有各种各样的虚拟机存在。
  • 例如性能最强悍的并不是上面所说的虚拟机,而是名为 Azul VM 和 BEA Liquid VM 的专用商业及虚拟机。这些虚拟机只运行在特定硬件平台,因此要求比较高。但其性能也是非常强悍的。其可以管理至少数十个 CPU 和数百 GB 的内存资源,还提供在巨大内存范围内实现可控 GC 时间的垃圾收集器等等。
  • 此外还有许许多多其他的虚拟机存在,例如:Apache Harmony、Google Android Dalvik VM、Mircosoft JVM 等等。
  • Oracle 看了这么些历史,似乎都是在说 Sun公司发布的虚拟机,与 Oracle 似乎没有什么关系。但在 2010 年,Oracle 公司收购了 Sun 公司,这样 Oracle 就拥有了 HotSpot VM。再加上其在 2008 年收购 BEA 公司获得的 JRocket VM,Oracle 公司就拥有了地球上最优秀的两款虚拟机。
  • 对于虚拟机未来的规划,Oracle 宣布会将 JRockit 的优秀特性整合到 HotSpot VM 中,例如移植 JRockit 的垃圾回收器和 MissionControl 服务。

第 3 讲 到底什么是虚拟机?

  • 为什么不同系统上的软件无法安装,这是因为操作系统底层的实现是不一样的。对于Windows系统来说,exe后缀的软件代码最终编译成Windows系统能识别的机器码。而Mac OSX 系统来说,dmg后缀的软件代码最终编译成 Mac OSX 系统能识别的代码。
  • 与其他语言不同,Java语言并不直接讲代码编译成与系统有关的机器码,而是编译成一种特定的语言规范,这种语言规范我们称之为字节码。无论Java程序要在Windows系统,还是Mac OSX系统,或者是Linux系统,它首先都得编译成字节码文件,之后才能运行。
  • 但即使编译成字节码文件了,各个系统还是无法明白字节码文件的内容,这时就需要Java虚拟机的帮助了。Java虚拟机会解析字节码文件的内容,并将其翻译为各操作系统能理解的机器码。
  • Java虚拟机运行的是字节码文件,如果你用php语言写一段代码,并自己用特定编译器能生成符合字节码规范的字节码文件,那么Java虚拟机也是可以运行的。
  • 简单来说Java虚拟机就是一个字节码翻译器,它将字节码文件翻译成各个系统对应的机器码,确保字节码文件能在各个系统正确运行。

第 4 讲 对于Java语言,从源代码到机器码,发生了什么?

编译器可以分为:前端编译器、JIT编译器和AOT编译器

前端编译器

前端编译器:源代码到字节码对于Java虚拟机来说,其实际输入的是字节码文件,而不是Java文件。怎样讲Java代码转化为字节码文件?我们知道在JDK的安装目录里有一个javac工具,就是它讲Java代码翻译成字节码,这个工具我们叫做编译器,因为是处于编译前期,所以被称为前端编译器。常见的前端编译器有Sun的javac,Eclipse JDT的增量编译器(ECJ)。通过javac编译器,我们可以很方便地将java源文件翻译成字节码文件。javac编译器解析Java源码,并生成字节码文件地过程,就是使用javac编译器把Java语言规范转化为字节码语言规范。

javac编译器地处理过程可以分为下面地四个阶段:

第一阶段:词法、语法分析

javac编译器会对源代码地字符进行一次扫描,最终生成一个抽象地语法树。

第二阶段:填充符号表

对抽象地类或接口进行符号填充,等到类加载阶段,javac编译器会将符号替换成具体地内存地址。

第三个阶段:注解处理

Java是支持注解地,因此在这个阶段会对注解进行分析,根据注解地作用将其还原成具体的指令集。

第四个阶段:分析与字节码生成

javac编译器会根据上面几个阶段分析出来的结果,进行字节码的生成,最终输出为class文件。

JIT编译器(即时编译器)

  • JIT编译器:从字节码到机器码
  • 当源代码转化为字节码后,其实要运行程序有两种选择:一种是Java解释器解释执行字节码,另一种则是使用JIT编译器将字节码转化为本地机器代码。两者区别在于,前者启动速度快但运行速度慢,后者启动速度慢但运行速度快。
  • 在HotSpot虚拟机内置了两个即时编译器,分别为Client Compiler和Server Compiler。
  • 这两种不同的编译器衍生出两种不同的编译模式,我们分别称之为:C1编译模式、C2编译模式(非官方说法)。

C1编译模式和C2编译模式的区别

  • C1编译模式会将字节码编译为本地代码,进行简单、可靠的优化,如:有必要将加入性能监控的逻辑。优化相对比较保守,比C2较快。
  • 而C2编译模式,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。C2的编译质量相对较好,但耗时更长。

对于HotSpot虚拟机有三种运行模式可选

  1. 混合模式:
  • C1和C2两种模式混合起来使用,这是默认的运行模式,如果想单独使用C1模式或C2模式,使用 -client 或 -server 打开即可。
  1. 解释模式:
  • 所有代码都解释执行,使用 -Xint 参数可以打开这个模式。
  1. 编译模式:
  • 此模式优先采用编译,但是无法编译时也会解释执行,使用 -Xcomp 打开这种模式。

此时,我们了解了从Java源代码到字节码,再从字节码到机器码的全过程,可以到这里就结束了。但是Java中还有一个AOT编译器,它能直接将源代码转换为机器码。

AOT编译器

  • AOT编译器:源代码到机器码
  • AOT编译器的基本思想是:在程序执行前生成Java方法的本地代码,以便在程序运行时直接使用本地代码,也就是在进入JVM之前就已经将本地机器码生成了。
  • 优点:启动速度快,减少运行时的开销
  • 限制:由于缺乏运行时信息,AOT编译质量也就是优化效果通常不如JIT编译。Java的动态类加载等特性可能会增加AOT编译的复杂性。

总结

  • 在JVM中有三个非常重要的编译器,它们分别是:前端编译器、JIT编译器、AOT编译器。
  • 前端编译器,最常见的是javac编译器,将Java源代码编译为Java字节码文件。
  • JIT编译器(即时编译器),最常见的是HotSpot虚拟机中的Client Compiler和 Server Compiler,将Java字节码编译成本地机器代码。
  • AOT编译器能将源代码直接编译为本地机器码。

编译速度和编译质量上的区别

  • 编译速度上,解释执行>AOT编译器>JIT编译器
  • 编译质量上,JIT编译器>AOT编译器>解释执行。

而在JVM中,通过这几种不同方式的配合,使得JVM的编译质量和运行速度达到最优的状态。

第 5 讲 字节码文件结构

《Java虚拟机规范》规定了Java虚拟机结构、Class类文件结构、字节码指令等内容。

字节码文件结构是一组以8位字节为基础的二进制流,各数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。

在字节码结构中,有两种最基本的数据类型来表示字节码文件格式,分别是:无符号数和表。

无符号数

  • 无符号数属于最基本的数据类型。它以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数。
  • 无符号数可以用来描述数字、索引引用、数量或者按照UTF-8编码构成的字符串值。

  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型。

一个Class字节码文件的格式内容

  1. magic: 魔数,固定为0xCAFEBABE,用于标识这是一个Java Class文件。
  2. minor_version 和 major_version: 分别表示Class文件的次版本号和主版本号。
  3. constant_pool_count: 常量池中常量的数量。
  4. constant_pool: 常量池,存储了类中用到的所有常量,包括字符串、类名、方法名等。这里除了基本类型和UTF-8的字符串存储的是值外,其他的都是存储的索引。
  5. access_flags: 访问标志,标识类或接口的访问权限和属性(如public, final, abstract等)。 如:这个Class是类还是接口、是否定义位public类型、是否定义为abstract类型等。标志值可以通过相加来叠加。
  6. this_class: 当前类的索引,确定这个类的全限定名,指向常量池中的一个项。
  7. super_class: 父类的索引,确定这个类的父类的全限定名,指向常量池中的一个项。
  8. interfaces_count 和 interfaces: 接口的数量和接口列表。Class文件中由this_class、super_class、interfaces_count这三项数据来确定这个类的继承关系。
  9. fields_count 和 fields: 字段的数量和字段列表。
  10. methods_count 和 methods: 方法的数量和方法列表。
  11. attributes_count 和 attributes: 属性的数量和属性列表。

欢迎关注 ❤

****************,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

感兴趣的朋友们可以加我微信:******************”,备注:【牛客】。

#Java面试##Java##面试中,你被问过哪些奇葩问题?##Java知识点总结#
Java知识点 文章被收录于专栏

包含Java知识的方方面面

全部评论

相关推荐

08-28 15:42
测试工程师
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务