写点什么

JVM 真香系列:轻松理解 class 文件到虚拟机(上)

用户头像
田维常
关注
发布于: 2020 年 11 月 08 日

关注“Java 后端技术全栈”


回复“000”获取大量电子书


认识 JVM

什么是 JVM


JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class 后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。


可能有部分小伙伴学习过 C++,C++开发出来的程序,编译成二进制文件后,就可以直接执行了,操作系统是能够识别的。


但是咱们开的的 Java 程序就不一样了,使用javac命令编译出来的的.class 文件之后,操作系统是不能识别的,需要对应JVM去做一个转换后,操作系统才能识别。


我们为什么不能像 C++ 一样,直接在操作系统上运行编译后的二进制文件呢?而非要搞一个处于程序与操作系统中间层的虚拟机呢?


这就是 JVM的过人之处了。大家都知道,Java 是一门抽象程度特别高的语言,提供了自动内存管理等一系列的特性。这些特性直接在操作系统上实现是不太可能的,所以就需要JVM 进行做一系列的转换。


大家一开始学 Java 的时候,就知道有个 Write Once, Run Everywhere。就是我们编写了一个 java 文件经过编译成.class 文件后,可以在各种系统中进行运行。


其实这里是有个前提的,我们需要在对应操作系统中安装对应的JVM,然后我们的.class 文件就能运行了。


比如:Windows 操作系统有对应的JDK安装版本、Linux 也有对应的JDK安装版本等。



认识 JDK


Java Development Kit (JDK) 是 Sun 公司(已被 Oracle 收购)针对 Java 开发员的软件开发工具包。自从 Java 推出以来,JDK已经成为使用最广泛的 Java SDK(Software development kit)。


经非官方调查,目前JDK8是使用者最多的版本。


JDK14将在 4 月和 7 月收到安全更新,然后由 9 月到期的非 LTS 版本的JDK 15取代。JDK14包括 16 项新功能,例如JDK Flight Recorder事件流,模式匹配和开关表达式等特征。


JDK9之后,Oracle 采用了新的发布周期:每 6 个月发布一个版本,每 3 年发布一个LTS版本。JDK14是继JDK9之后发布的第四个版本, 该版本为非LTS版本,最新的LTS版本为JDK11


下面是JDK版本情况


这个混个眼熟就行,随时关注JDK版本更新和新特性。


官网地址:https://www.oracle.com/java/


关于JDK安装这里就省略。


JDK、JRE、JVM 的关系


上面已经说过JDKJVM的相关概念,


JRE全程 Java Runtime Environment,是运行基于 Java 语言编写的程序所不可缺少的运行环境。也是通过它,Java 的开发者才得以将自己开发的程序发布到用户手中,让用户使用。


三者到底是什么关系呢?


关于三者关系请看官网


https://docs.oracle.com/javas...


JDK中包含JRE,也包括JDK,而JRE也包括JDK。范围关系:JDK>JRE>JVM


".java"文件到".class"文件


javac命令


编写一个HelloWorld.java文件


内容就是一个Java入门


public class HelloWorld {    public static void main(String[] args) {        System.out.println("Hello world");    }}
复制代码


打开CMD,进入当前目录,使用命令


javac HelloWorld.java
复制代码


就编译出HelloWorld.class



编译过程


这个 javac 命令过程到底干了些什么呢?


javac背后大致做了这些操作



这个流程



1、词法分析


读取源代码,一个字节一个字节的读取,找出其中我们定义好的关键字(如Java中的 if、else、for、while 等关键词,识别哪些 if 是合法的关键词,哪些不是),这就是词法分析器进行词法分析的过程,其结果是从源代码中找出规范化的 Token 流。


2、语法分析


通过语法分析器对词法分析后 Token 流进行语法分析,这一步检查这些关键字组合再一次是否符合Java语言规范(如在 if 后面是不是紧跟着一个布尔判断表达式),词法分析的结果是形成一个符合Java语言规范的抽象语法树。


3、语义分析


通过语义分析器进行语义分析。语音分析主要是将一些难懂的、复杂的语法转化成更加简单的语法,结果形成最简单的语法(如将foreach转换成 for 循环 ,好有注解等),最后形成一个注解过后的抽象语法树,这个语法树更为接近目标语言的语法规则。


4、生成字节码


通过字节码生产器生成字节码,根据经过注解的语法抽象树生成字节码,也就是将一个数据结构转化为另一个数据结构。最后生成我们想要的.class 文件。


使用十六进制查看 class 文件内容


我只用的是 Notepad++,选中文本→插件→Converter→ASCII->HEX


class 文件的开头就是


CAFEBABE


想要学习这里的十六进制的字节码的含义可以参考


https://docs.oracle.com/javas...


-


javap 查看 class 文件内容


javap是 Java class 文件分解器,可以反编译(即对javac编译的文件进行反编译),也可以查看java编译器生成的字节码。


新建一个User.java源文件,经过javac编译后,生成User.classs


package com.tian.demo.test;public class User {    private int age = 22;    private String name = "tian";    public int addAge() {        return age = age + 1;    }    public static void main(String[] args) {    }}
复制代码


使用javap命令


javap -v User.class >log.txt
复制代码


打开log.txt


Classfile /D:/workspace/new/demo/src/main/java/com/tian/demo/test/User.class  Last modified 2020-11-5; size 441 bytes  MD5 checksum 2fa72d3f53bd9f138e0bfae82aba67e3  Compiled from "User.java"public class com.tian.demo.test.User  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V   #2 = Fieldref           #5.#22         // com/tian/demo/test/User.age:I   #3 = String             #23            // tian   #4 = Fieldref           #5.#24         // com/tian/demo/test/User.name:Ljava/lang/String;   #5 = Class              #25            // com/tian/demo/test/User   #6 = Class              #26            // java/lang/Object   #7 = Utf8               age   #8 = Utf8               I   #9 = Utf8               name  #10 = Utf8               Ljava/lang/String;  #11 = Utf8               <init>  #12 = Utf8               ()V  #13 = Utf8               Code  #14 = Utf8               LineNumberTable  #15 = Utf8               addAge  #16 = Utf8               ()I  #17 = Utf8               main  #18 = Utf8               ([Ljava/lang/String;)V  #19 = Utf8               SourceFile  #20 = Utf8               User.java  #21 = NameAndType        #11:#12        // "<init>":()V  #22 = NameAndType        #7:#8          // age:I  #23 = Utf8               tian  #24 = NameAndType        #9:#10         // name:Ljava/lang/String;  #25 = Utf8               com/tian/demo/test/User  #26 = Utf8               java/lang/Object{  public com.tian.demo.test.User();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=2, locals=1, args_size=1         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: aload_0         5: bipush        22         7: putfield      #2                  // Field age:I        10: aload_0        11: ldc           #3                  // String tian        13: putfield      #4                  // Field name:Ljava/lang/String;        16: return      LineNumberTable:        line 3: 0        line 4: 4        line 5: 10  public int addAge();    descriptor: ()I    flags: ACC_PUBLIC    Code:      stack=3, locals=1, args_size=1         0: aload_0         1: aload_0         2: getfield      #2                  // Field age:I         5: iconst_1         6: iadd         7: dup_x1         8: putfield      #2                  // Field age:I        11: ireturn      LineNumberTable:        line 8: 0  public static void main(java.lang.String[]);    descriptor: ([Ljava/lang/String;)V    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=0, locals=1, args_size=1         0: return      LineNumberTable:        line 13: 0}SourceFile: "User.java"
复制代码


魔数与 class 文件版本

常量池

访问标志

类索引、父类索引、接口索引

字段表集合

方法表集合

属性表集合


然后JVM就可以读取这个User.class文件进行解析等一系列的操作。


以上就是我们的Java文件到 class 文件。


后续还会更新一系列 JVM 相关文章,敬请期待~


推荐阅读:


《Java Web企业项目实战》.pdf


《MySQL开发者SQL权威指南》.pdf


关注公众号“Java 后端技术全栈”


免费获取 500G 最新学习资料



发布于: 2020 年 11 月 08 日阅读数: 27
用户头像

田维常

关注

关注公众号:Java后端技术全栈,领500G资料 2020.10.24 加入

关注公众号:Java后端技术全栈,领500G资料

评论

发布
暂无评论
JVM真香系列:轻松理解class文件到虚拟机(上)