一探究竟:为什么需要 JVM?它处在什么位置?

news2024/11/28 10:55:19

小熊学Java全能学习+面试指南:https://www.javaxiaobear.cn/

JVM我们并不陌生,现在我们就正式进入 JVM 的学习,如果你是一名软件开发工程师,在日常工作中除了 Java 这个关键词外,还有一个名词也一定经常被提及,那就是 JVM。提到 JVM 我们经常会在面试中遇到这样的问题:

  • 为什么 Java 研发系统需要 JVM?
  • 对你 JVM 的运行原理了解多少?
  • 我们写的 Java 代码到底是如何运行起来的?

想要在面试中完美地回答这三个问题,就需要首先了解 JVM 是什么?它和 Java 有什么关系?又与 JDK 有什么渊源?接下来,我就带你拨开这些问题的层层迷雾,想要弄清楚这些问题,我们首先需要从这三个维度去思考:

  • JVM 和操作系统的关系?
  • JVM、JRE、JDK 的关系?
  • Java 虚拟机规范和 Java 语言规范的关系?

弄清楚这几者的关系后,我们再以一个简单代码示例来看一下一个 Java 程序到底是如何执行的。

JVM 和操作系统的关系

在武侠小说中,想要炼制一把睥睨天下的宝剑,是需要下一番功夫的。除了要有上等的铸剑技术,还需要一鼎经百炼的剑炉,而工程师就相当于铸剑的剑师,JVM 便是剑炉。

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

一般情况下,使用 C++ 开发的程序,编译成二进制文件后,就可以直接执行了,操作系统能够识别它;但是 Java 程序不一样,使用 javac 编译成 .class 文件之后,还需要使用 Java 命令去主动执行它,操作系统并不认识这些 .class 文件。

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

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

有了上面的介绍,我们就可以做如下的类比。

  • JVM:等同于操作系统;
  • Java 字节码:等同于汇编语言。

Java 字节码一般都比较容易读懂,这从侧面上证明 Java 语言的抽象程度比较高。你可以把 JVM 认为是一个翻译器,会持续不断的翻译执行 Java 字节码,然后调用真正的操作系统函数,这些操作系统函数是与平台息息相关的。

如果你还是对上面的介绍有点模糊,可以参考下图:

从图中可以看到,有了 JVM 这个抽象层之后,Java 就可以实现跨平台了。JVM 只需要保证能够正确执行 .class 文件,就可以运行在诸如 Linux、Windows、MacOS 等平台上了。

而 Java 跨平台的意义在于一次编译,处处运行,能够做到这一点 JVM 功不可没。比如我们在 Maven 仓库下载同一版本的 jar 包就可以到处运行,不需要在每个平台上再编译一次。

现在的一些 JVM 的扩展语言,比如 Clojure、JRuby、Groovy 等,编译到最后都是 .class 文件,Java 语言的维护者,只需要控制好 JVM 这个解析器,就可以将这些扩展语言无缝的运行在 JVM 之上了。

我们用一句话概括 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,它的中间接口就是字节码。

而 Java 程序和我们通常使用的 C++ 程序有什么不同呢?这里用两张图进行说明。

对比这两张图可以看到 C++ 程序是编译成操作系统能够识别的 .exe 文件,而 Java 程序是编译成 JVM 能够识别的 .class 文件,然后由 JVM 负责调用系统函数执行程序。

JVM、JRE、JDK的关系

通过上面的学习我们了解到 JVM 是 Java 程序能够运行的核心。但是需要注意,JVM 自己什么也干不了,你需要给它提供生产原料(.class 文件)。俗语说的好,巧妇难为无米之炊。它虽然功能强大,但仍需要为它提供 .class 文件。

仅仅是 JVM,是无法完成一次编译,处处运行的。它需要一个基本的类库,比如怎么操作文件、怎么连接网络等。而 Java 体系很慷慨,会一次性将 JVM 运行所需的类库都传递给它。JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境,也就是我们常说的 JRE(Java Runtime Environment)。

有了 JRE 之后,我们的 Java 程序便可以在浏览器中运行了。大家可以看一下自己安装的 Java 目录,如果是只需要执行一些 Java 程序,只需要一个 JRE 就足够了。

对于 JDK 来说,就更庞大了一些。除了 JRE,JDK 还提供了一些非常好用的小工具,比如 javac、java、jar 等。它是 Java 开发的核心,让外行也可以炼剑!

我们也可以看下 JDK 的全拼,Java Development Kit。我非常怕 kit(装备)这个单词,它就像一个无底洞,预示着你永无休止的对它进行研究。JVM、JRE、JDK 它们三者之间的关系,可以用一个包含关系表示。

  • JDK>JRE>JVM

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

我们通常谈到 JVM,首先会想到它的垃圾回收器,其实它还有很多部分,比如对字节码进行解析的执行引擎等。广义上来讲,JVM 是一种规范,它是最为官方、最为准确的文档;狭义上来讲,由于我们使用 Hotspot 更多一些,我们一般在谈到这个概念时,会将它们等同起来。

如果再加上我们平常使用的 Java 语言的话,可以得出下面这样一张图。这是 Java 开发人员必须要搞懂的两个规范。

左半部分是 Java 虚拟机规范,其实就是为输入和执行字节码提供一个运行环境。右半部分是我们常说的 Java 语法规范,比如 switch、for、泛型、lambda 等相关的程序,最终都会编译成字节码。而连接左右两部分的桥梁依然是 Java 的字节码。

如果 .class 文件的规格是不变的,这两部分是可以独立进行优化的。但 Java 也会偶尔扩充一下 .class 文件的格式,增加一些字节码指令,以便支持更多的特性。

我们可以把 Java 虚拟机可以看作是一台抽象的计算机,它有自己的指令集以及各种运行时内存区域,学过《计算机组成结构》的同学会在课程的后面看到非常多的相似性。

你可能会有疑问,如果我不学习 JVM,会影响我写 Java 代码么?理论上,这两者没有什么必然的联系。它们之间通过 .class 文件进行交互,即使你不了解 JVM,也能够写大多数的 Java 代码。就像是你写 C++ 代码一样,并不需要特别深入的了解操作系统的底层是如何实现的。

但是,如果你想要写一些比较精巧、效率比较高的代码,就需要了解一些执行层面的知识了。了解 JVM,主要用在调优以及故障排查上面,你会对运行中的各种资源分配,有一个比较全面的掌控。

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

最后,我们简单看一下一个 Java 程序的执行过程,它到底是如何运行起来的。

这里的 Java 程序是文本格式的。比如下面这段 HelloWorld.java,它遵循的就是 Java 语言规范。其中,我们调用了 System.out 等模块,也就是 JRE 里提供的类库。

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

使用 JDK 的工具 javac 进行编译后,会产生 HelloWorld 的字节码。

我们一直在说 Java 字节码是沟通 JVM 与 Java 程序的桥梁,下面使用 javap 来稍微看一下字节码到底长什么样子。

0 getstatic #2 <java/lang/System.out>
3 ldc #3 <Hello World>
5 invokevirtual #4 <java/io/PrintStream.println>
8 return

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

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

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 <java/lang/System.out>。

JVM 就是靠解析这些 opcode 和操作数来完成程序的执行的。当我们使用 Java 命令运行 .class 文件的时候,实际上就相当于启动了一个 JVM 进程。

然后 JVM 会翻译这些字节码,它有两种执行方式。常见的就是解释执行,将 opcode + 操作数翻译成机器代码;另外一种执行方式就是 JIT,也就是我们常说的即时编译,它会在一定条件下将字节码编译成机器码之后再执行。

这些 .class 文件会被加载、存放到 metaspace 中,等待被调用,这里会有一个类加载器的概念。

而 JVM 的程序运行,都是在栈上完成的,这和其他普通程序的执行是类似的,同样分为堆和栈。比如我们现在运行到了 main 方法,就会给它分配一个栈帧。当退出方法体时,会弹出相应的栈帧。你会发现,大多数字节码指令,就是不断的对栈帧进行操作。

而其他大块数据,是存放在堆上的。Java 在内存划分上会更为细致,关于这些概念,我们会在接下来的课时里进行详细介绍。

最后大家看下面的图,其中 JVM 部分,就是我们课程的要点。

选用的版本

既然 JVM 只是一个虚拟机规范,那肯定有非常多的实现。其中,最流行的要数 Oracle 的 HotSpot。

目前,最新的版本是 Java13(注意最新的LTS版本是11)。学技术当然要学最新的,我们以后的课时就以 13 版本的 Java 为基准,来讲解发生在 JVM 上的那些事儿。

为了完成这个过程,你可以打开浏览器,输入下载网址(https://www.oracle.com/technetwork/java/ javase/downloads/jdk13-downloads-5672538.html)并安装软件。当然你也可以用稍低点的版本,但是有些知识点会有些许差异。相信对于聪明的你来说,这写都不算问题,因为整个 JVM,包括我们的调优,就是在不断试错中完成的。

小结

我们再回头看看上面的三个问题。

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

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

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

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

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

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

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

总结

到这里内容就全部讲完了,今天我们分别从三个角度,了解了 JVM 在 Java 研发体系中的位置,并以一个简单的程序,看了下一个 Java 程序基本的执行过程。

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

JVM 是一个非常小的集合,我们常说的 Java 运行时环境,就包含 JVM 和一部分基础类库。如果加上我们常用的一些开发工具,就构成了整个 JDK。我们讲解 JVM 就聚焦在字节码的执行上面。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/968202.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3、Spring 之IOC 容器 详解

IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反转”&#xff0c;它不是一门技术&#xff0c;而是一种设计思想&#xff0c;是一个重要的面向对象编程法则&#xff0c;能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器来管理所有 Java 对象的…

【SpringSecurity】十二、集成JWT搭配Redis实现退出登录

文章目录 1、登出的实现思路2、集成Redis3、认证成功处理器4、退出成功处理器5、修改token校验过滤器6、调试 1、登出的实现思路 这是目前的token实现图&#xff1a; 因为JWT的无状态&#xff0c;服务端无法在使用过程中主动废止某个 token&#xff0c;或者更改 token 的权限…

《TCP/IP网络编程》阅读笔记--基于Windows实现Hello Word服务器端和客户端

目录 1--Hello Word服务器端 2--客户端 3--编译运行 3-1--编译服务器端 3-2--编译客户端 3-3--运行 1--Hello Word服务器端 // gcc hello_server_win.c -o hello_server_win -lwsock32 // hello_server_win 9190 #include <stdio.h> #include <stdlib.h> #i…

07-ThreadLocal有哪些使用场景?【Java面试题总结】

ThreadLocal有哪些使用场景&#xff1f; 7.1 多线程场景下共享变量问题 ThreadLocal是线程本地变量&#xff0c;可以存储共享变量副本&#xff0c;每一个独立线程都有与共享变量一模一样的副本。ThreadLocal在当前线程下共享变量是全局共享的&#xff0c;各个线程之间是相互独…

虚拟内存相关笔记

虚拟内存是计算机系统内存管理的一个功能&#xff0c;它允许程序认为它们有比实际物理内存更多的可用内存。它使用硬盘来模拟额外的RAM。当物理内存不足时&#xff0c;操作系统将利用磁盘空间作为虚拟内存来存储数据。这种机制提高了资源的利用率并允许更大、更复杂的应用程序的…

ICCV 2023 | TUM谷歌提出md4all:挑战性条件下的单目深度估计

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【深度估计】交流群 Robust Monocular Depth Estimation under Challenging Conditions 作者列表: Stefano Gasperini, Nils Morbitzer, HyunJun Jung, Nassir Navab, Federi…

【小作文】【信】

【邀请信】【22&#xff0c;1邀请教授参加比赛】 【投诉信】【12,2投诉产品质量问题】

一般不用buildroot来编译uboot和kernel

Buildroot 是一个流行的嵌入式 Linux 系统构建工具&#xff0c;它可以帮助开发者自动化地构建完整的嵌入式 Linux 系统&#xff0c;包括文件系统、内核以及各种用户空间应用程序。虽然 Buildroot 在构建嵌入式系统方面非常强大且易于使用&#xff0c;但一般情况下&#xff0c;它…

STM32WB55开发(1)----套件概述

STM32WB55开发----1.套件概述 所用器件视频教学样品申请优势支持协议系统控制和生态系统访问功能示意图系统框图跳线设置开发板原理图 所用器件 所使用的器件是我们自行设计的开发板&#xff0c;该开发板是基于 STM32WB55 系列微控制器所构建。STM32WBXX_VFQFPN68 不仅是一款评…

Linux学习之NAS服务器搭建

NAS是Network Attached Storage的缩写&#xff0c;也就是网络附属存储。可以使用自己已经不怎么使用的笔记本搭建一台NAS服务器。 fdisk -l可以看一下各个磁盘的状态。 可以看到有sda、sdb、sdc和sdd等四块硬盘。 lvs、vgs和pvs结合起来看&#xff0c;sdb和sdc没有被使用。 …

MYSQL_

文章目录 ①. 索引的概述②. 二叉树和红黑树③. Hash建立索引结构④. B树的数据结构⑤. MyISAM存储引擎索引实现⑥. InnoDB索引实现(聚集)⑦. 联合索引的设定 ①. 索引的概述 ①. 索引是帮助MySQL高效获取数据的排好序的数据结构 ②. mysql数据库的实现原理通过b树实现的,b树的…

docker安装redis,并挂载配置文件

1&#xff1a;下载镜像&#xff0c;不添加版本 默认下载最新的 docker pull redis下载成功后如图所示 2&#xff1a;下载redis配置文件&#xff0c;我是在docker中下载的&#xff0c;也可以使用文件上传工具将配置文件上传到自己指定的目录。 首先需要安装wget&#xff0c;否…

第一章 USB应用笔记之USB初步了解

USB应用笔记之USB初步了解 文章目录 USB应用笔记之USB初步了解前言USB的优点&#xff1a;USB版本发展USB速度以及电气接口USB传输过程USB开发抓包工具&#xff1a;USB传输方式1.控制传输特点:2.中断传输的特点3. 批量传输的特点4.实时传输&#xff08;同步传输&#xff09;的特…

同步与互斥

硬件指令 实现互斥&#xff1a;硬件指令&#xff0c;硬件实现的原子操作&#xff0c;不会被打断 tsl指令和xchg指令 当前指令执行完&#xff0c;才会检测中断 If the signal comes while an instruction is being executed, it is held until the execution of the instructi…

Feign负载均衡写法

Feign主要为了面向接口编程 feign是web service客户端&#xff0c;是接口实现的&#xff0c;而ribbon是通过微服务名字访问通过RestTemplate调用的&#xff0c;如下&#xff1a; 在Feign的实现下&#xff0c;我们只需要创建一个接口并使用注解的方式来配置它&#xff08;类似…

仿京东 项目笔记2(注册登录)

这里写目录标题 1. 注册页面1.1 注册/登录页面——接口请求1.2 Vue开发中Element UI的样式穿透1.2.1 ::v-deep的使用1.2.2 elementUI Dialog内容区域显示滚动条 1.3 注册页面——步骤条和表单联动 stepsform1.4 注册页面——滑动拼图验证1.5 注册页面——element-ui组件Popover…

开开心心带你学习MySQL数据库之第三篇上

学校的项目组有必要加入吗? 看你的初心. ~~如果初心是通过这个经历能够提高自己的技术水平 ~~是可以考虑的 ~~如果初心是通过这个经历提高自己找工作的概率 ~~这个是不靠谱的,啥用没有 ~~如果初心是通过这个体验更美好的大学生活 ~~靠谱的 秋招,应届生,找工作是非常容易的!!! …

《开发实战》13 | 用好Java 8的日期时间类,少踩一些“老三样”的坑

13 | 用好Java 8的日期时间类&#xff0c;少踩一些“老三样”的坑 初始化日期时间 如果要初始化一个 2019 年 12 月 31 日 11 点 12 分 13秒这样的时间&#xff0c;Date date new Date(2019, 12, 31, 11, 12, 13);输出的时间是 3029 年 1 月 31 日 11 点 12 分 13 秒&#xf…

时间语义与窗口

时间语义 在Flink中&#xff0c;时间语义分为两种 &#xff1a; 处理时间和事件时间。时间语义与窗口函数是密不可分的。以窗口为单位进行某一段时间内指标统计&#xff0c;例如想要统计8点-9点的某个页面的访问量&#xff0c;此时就需要用到了窗口函数&#xff0c;这里的关键…

【倒着考虑】CF Edu 21 D

Problem - D - Codeforces 题意&#xff1a; 思路&#xff1a; 这道题需要倒着步骤考虑&#xff0c;就是先去假设已经分为了两部分&#xff0c;这左右两部分的和相等&#xff0c;然后去想上一个步骤 倒着一个步骤后&#xff0c;可以发现这样的性质&#xff1a; Code&#xf…