Java web

18. java虚拟机总结-JVM介绍(二)

2020-04-10  本文已影响0人  任振铭

JVM 就是Java虚拟机(Java Runtime Machine),能够识别.class后缀的文件,解析他的指令,最终调用操作系统上的函数,完成我们需要的操作。

JVM 和操作系统的关系

Java 程序使用 javac 编译成 .class 文件之后,还需要使用 Java 命令去主动执行它,否则操作系统无法识别这些 .class 文件,Java 虚拟机就是一个中间层的角色,承担了解析.class文件的责任,它能够解析.class的指令,最终调用操作系统上的函数。

一句话概括 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,是一个中间商的角色,有了它就可以实现跨平台了。

Java为什么不像c++一样直接在操作系统上运行编译后的二进制文件,而非要做一个处于中间层的JVM出来?因为Java是一个抽象度特别高的语言,提供了自动内存管理等一系列特性,这些无法通过操作系统实现,所以需要JVM做转换

jvm和操作系统的关系.png

JVM、JRE、JDK的关系

上边说了JVM是用来处理.class文件的,.class就是JVM的输入原料,所以仅仅是 JVM,是无法完成一次编译,处处运行的,.class从何而来?它需要一个基本的类库,比如怎么操作文件、怎么连接网络等,这些JVM 运行所需的类库就是通过JRE提供的

1.JRE : Java Runtime Environment = 类库+JVM

JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境---JRE(Java Runtime Environment)。有了 JRE 之后,Java 程序便可以运行了。可以看一下安装的 Java 目录,如果是只需要执行一些 Java 程序,只需要一个 JRE 就足够了

2.JDK : Java Development Kit = JRE+工具

JDK包含JRE,除此之外还提供了一些非常好用的小工具,比如 javac、java、jar 等,是 Java 开发的核心

JVM : Java Runtime Machine

JDK JRE JVM的关系.png

Java 虚拟机规范和 Java 语言规范的关系

Java 虚拟机规范,其实就是为输入和执行字节码提供一个运行环境,Java 语言规范,指的是我们开发Java程序中编码时需要注意的规范。Java程序被最终编译成字节码,输入到Java虚拟机中进行解析执行,二者的联系就是字节码


Java虚拟机规范和Java语言规范.png

Java 代码是如何被运行起来的

public class HelloWorld{
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

我们把上边代码反编译,命令: javap -c HelloWorld.class c表示对代码进行反编译

public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello World
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

Java虚拟机采用基于栈的架构,其指令由操作码和操作数组成。这些字节码指令,就叫作opcode。其中,getstatic、ldc、invokevirtual、return等,就是opcode

使用 hexdump 看一下main方法中字节码的二进制内容。与以上字节码对应的二进制,就是下面这几个数字(可以搜索一下)。

b2 00 02 12 03 b6 00 04 b1

看一下它们的对应关系。

0xb2   getstatic   获取静态字段的值
0x12    ldc   常量池中的常量值入栈
0xb6   invokevirtual   运行时方法绑定调用
0xb1    return           void 函数返回

opcode 有一个字节的长度(0~255),意味着指令集的操作码个数不能操作 256 条。而紧跟在 opcode 后面的是被操作数。比如 b2 00 02,就代表了 getstatic #2

JVM靠解析这些opcode和操作数来运行程序,我们使用Java命令运行.class文件就相当于启动了一个JVM进程,然后JVM会翻译这些字节码,通过两种方式,1.解释执行(解析模式),将opcode和操作数解析成机器代码,或者2.JIT(即时编译,编译模式),它会在一定条件下将字节码编译成机器码之后再执行。这些.class文件会被加载、存放到metaspace中,等待被调用,这里会有一个类加载器的概念。而JVM的程序运行,都是在栈上完成的,这和其他普通程序的执行是类似的,同样分为堆和栈。比如我们现在运行到了 main 方法,就会给它分配一个栈帧。当退出方法体时,会弹出相应的栈帧。你会发现,大多数字节码指令,就是不断的对栈帧进行操作。而其他大块数据,是存放在堆上的。


java程序运行过程.png

解释模式、编译模式、混合模式

jvm中的及时编译器 JIT 有三种编译模式:解释执行模式、编译执行模式、混合执行模式。(编译执行模式效率远远高于解释执行模式)

// mixed mode 表示是混合执行模式
C:\Users\Administrator>java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) Client VM (build 25.144-b01, mixed mode, sharing)

可以通过命令切换为编译执行模式:java -Xcomp -version。该模式会将函数体编译成机器码,每次函数执行只执行编译好的机器码,效率很高。
编译执行的缺点是不加筛选的将全部代码编译成机器码而不考虑其执行频率是否由编译的价值,在程序有响应时间的限制下,编译器没法采用耗时较高的优化技术(因为JIT的编译是首次运行或启动的时候进行的),所以纯编译模式下Java程序的执行效率和c c++相比仍有差距

C:\Users\Administrator>java -Xcomp -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) Client VM (build 25.144-b01, compiled mode, sharing)

通过命令切换为解析模式:java -Xint -version,该模式不经过jit直接由解释器解释执行所有字节码,效率低于编译模式

C:\Users\Administrator>java -Xint -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) Client VM (build 25.144-b01, interpreted mode, sharing)

通过命令切换为混合模式(默认都是混合模式):java -Xmixed -version,该模式是混合使用解析模式和编译模式,执行时会判断执行函数调用频率,若频率高则是热点代码,使用编译模式执行,否则则使用解析模式执行

C:\Users\Administrator>java -Xmixed -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) Client VM (build 25.144-b01, mixed mode)

三个问题。

为什么 Java 研发系统需要 JVM?

JVM 解释的是类似于汇编语言的字节码,需要一个抽象的运行时环境。同时,这个虚拟环境也需要解决字节码加载、自动垃圾回收、并发等一系列问题。JVM 其实是一个规范,定义了 .class 文件的结构、加载机制、数据存储、运行时栈等诸多内容,最常用的 JVM 实现就是 Hotspot。

对你 JVM 的运行原理了解多少?

JVM 的生命周期是和 Java 程序的运行一样的,当程序运行结束,JVM 实例也跟着消失了。JVM 处于整个体系中的核心位置,关于其具体运行原理,我们在下面的课时中详细介绍。

我们写的 Java 代码到底是如何运行起来的?

一个 Java 程序,首先经过 javac 编译成 .class 文件,然后 JVM 将其加载到元数据区,执行引擎将会通过混合模式执行这些字节码。执行时,会翻译成操作系统相关的函数。JVM 作为 .class 文件的黑盒存在,输入字节码,调用操作系统函数。

过程如下:Java 文件->编译器>字节码->JVM->机器码。

选用的JVM版本

JVM 只是一个虚拟机规范,有非常多的实现。最流行的要数 Oracle 的 HotSpot。目前,最新的版本是 Java13(注意最新的LTS版本是11)。以 13 版本的 Java 为基准,来了解发生在 JVM 上的那些事儿。

我们所说的JVM,狭义上指的就HotSpot。如非特殊说明,我们都以HotSpot为准。我们了解到,Java之所以成为跨平台,就是由于JVM的存在。Java的字节码,是沟通 Java 语言与 JVM 的桥梁,同时也是沟通 JVM 与操作系统的桥梁。

Java 虚拟机采用基于栈的架构,有比较丰富的 opcode。这些字节码可以解释执行,也可以编译成机器码,运行在底层硬件上,可以说 JVM 是一种混合执行的策略。

总结:

一个Java程序,首先经过javac编译成.class文件,然后JVM将其加载到元数据区,执行引擎将会执行这些字节码。执行时,会翻译成操作系统相关的函数。JVM 作为 .class 文件的黑盒存在,输入字节码,调用操作系统函数。过程如下:Java 文件->编译器>字节码->JVM->机器码。

上一篇下一篇

猜你喜欢

热点阅读