什么是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的关系
上面已经说过JDK和JVM的相关概念,
JRE全程Java Runtime Environment,是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
三者到底是什么关系呢?
关于三者关系请看官网
https://docs.oracle.com/javase/8/docs/index.html
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/javase/specs/jvms/se8/html/jvms-4.html
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_SUPER
- Constant 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文件。
IT技术分享社区
个人博客网站:https://programmerblog.xyz认识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的关系
上面已经说过JDK和JVM的相关概念,
JRE全程Java Runtime Environment,是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
三者到底是什么关系呢?
关于三者关系请看官网
https://docs.oracle.com/javase/8/docs/index.html
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/javase/specs/jvms/se8/html/jvms-4.html
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_SUPER
- Constant 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文件。