阅读本文大概须要 5.6 分钟java
在 Java 开发中,咱们常常会提到 JVM。咱们知道 JVM 是 Java 虚拟机,可是它的运行原理是什么?它的内存结构是什么?如何进行优化?如何去定位问题?面试中遇到 JVM 问题如何回答?linux
接下来我会开启 JVM 的章节,为你们一一解答上面的问题。如今就开启咱们的 JVM 学习之路吧!面试
面试官:什么是 JVM?windows
小李:JVM(Java Virtual Machine)是 Java 虚拟机,用于运行 Java 编译后的二进制字节码,最后生成机器指令。(内心一想,简简单单)数组
面试官:那为何 Java 研发体系须要 JVM?你对 JVM 的运行原理了解多少?咱们写的 Java 代码究竟是如何运行起来的?bash
小李:嗯。。。就是。。。嗯。。。是那个。。。嗯。。。网络
面试官:面试就到这里了,先回去等通知吧。架构
小李:好的!(哭着回答)并发
这里面试官对小李进行三连问:app
这套组合拳看似很厉害,其实就是军体拳。
若是想完美的练这套军体拳,不,是完美的回答这三个问题,就须要首先要了解 JVM 是什么?它和 Java 是什么关系?又和 JDK 有什么渊源?那要弄清楚这些问题,就须要从三个维度去思考:
弄清楚这这几者的关系,咱们再经过一个简单代码示例来看一个 Java 程序究竟是如何执行的。
咱们知道炼制一把牛逼的大宝剑,不只须要上等的技术,还须要一鼎经百炼的剑炉。而工程师就至关于铸剑的剑师,JVM 即是剑炉。
JVM 就是咱们耳熟能详的 Java 虚拟机。它能识别 .class 后缀文件,而且可以解析它的指令,最终调用操做系统上的函数,完成咱们想要的操做。
Java 程序和 C++ 程序有什么不一样呢?这里用两张图进行说明。
对比两张图能够看到 C++ 开发的程序能够翻译成操做系统能识别的 .exe 文件。而 Java 程序须要经过 javac 编译成 .class 文件以后,而后由 JVM 负责调用系统函数执行程序,操做系统并不认识 .class 文件。
那读者就劝小李了,转 C++ 开发吧,这 Java 还搞了一个处于程序和操做系统的虚拟机,不像 C++ 编译后直接在操做系统上运行,确定不是啥好玩意。
我就知道大家坏的很,知道 JVM 的过人之处,还不告诉小李。那我给小李讲讲 JVM 的过人之处:
Java 是一门抽象度特别高的语言,提供了自动内存管理等一系列的特性。这些特性在操做系统上基本上是无望了,因此就须要 JVM 进行一番转换。
通过上面的介绍,咱们能够作以下的类比:
Java 字节码仍是比较容易读懂,从侧面上也证实了 Java 语言的抽象程度高。咱们能够认为 JVM 是一个翻译器,会持续不断的翻译执行 Java 字节码,而后调用真正的操做系统函数,这些操做系统函数是与平台息息相关的。
能够把 JVM 想象一个有道词典,.class 文件是英文,而输出的结果是中文。有道词典有 windows版本,也有 Linux 版本,内部具体的实现确定不一样,但最终都会获得相同的结果,这样就好理解一些了)
当有个 JVM 这个抽象层,就能够实现跨平台了。JVM 只须要正确执行 .class 文件,就能够运行在 Linux、Windos、MacOS 等平台了。
Java 跨平台的意义在于一次编译,到处运行,这里 JVM 功不可没。好比在 Maven 仓库下载的 jar 包就能够处处运行,不须要在每一个平台上再编译一次。
咱们来归纳 JVM 与操做系统之间的关系:
JVM 上承开发语言,下接操做系统,它的中间接口就是字节码。
经过上面的学习,咱们了解到 JVM 是 Java 程序可以运行的核心。可是咱们要知道,JVM 本身什么也干不了,你须要给它提供原料(.class 文件)。俗话说:巧妇难为无米之炊。JVM 功能虽然强大,但仍是须要为它提供 .class 文件。
可是仅靠 JVM 是没法完成一次编译,处处运行的。它须要一个基本的类库,好比怎么操做文件、怎么链接网络、怎么教你出拳(小李已疯)等。而 Java 体系会一次性将 JVM 运行所需的类库都传递给它。JVM 标准加上基本类库就组成了 Java 的运行环境,就是 JRE (Java Runtime Enviroment)
JVM + 基本类库 = JRE
那 JDK 又是什么呢?
JDK 全称 Java Development Kit,Kit 是装备的意思。因此 JDK 不只包含 JRE,还有一些小工具,好比 javac、java、jar等。
JRE + javac/java/jar 等指令工具 = JDK
JVM、JRE、JDK 它们三者之间的关系,能够用一个包含关系表示。
从广义上来说,JVM 是一种规范,它是最为官方、准确的文档;狭义上来说,因为咱们使用 Hotspot 更多一些,因此咱们在谈到这个概念时,会将他们等同起来。
若是再加咱们日常使用的 Java 语言,能够获得下面一张图。
左边是 Java 虚拟机规范,为字节码的解析提供一个环境。右边是 Java 语法规范,好比 switch、for、泛型、lambda 等相关的程序,最终都会编译成字节码。而字节码是连接左右两部分的桥梁。
若是 .class 文件的规格是不变的,这两部分是能够独立进行优化的。But 没有若是,如今都已经到 Java 13 了,为了支持更多的特性,确定会增长一些字节码指令。
此刻优秀的小李提出了一个让人深思的问题:
若是我不学习 JVM,会影响我写 Java 代码么?
理论上,这二者没有必然的联系。他们之间经过 .class 文件进行交互,即便你不了解 JVM,也可以写大多数的 Java 代码。就像你是写 C++ 代码同样,并不须要特别深刻的了解操做系统的底层是如何实现的。
那我还学个锤子!瞬间关了该页面。
客官别走,还有可是没说呢。
可是,若是你想要写一些比较精巧、效率比较高的代码,就须要了解一些执行层面的知识了。了解 JVM,主要用在调优以及故障排查上面,你会对运行中的各类资源分配,有一个比较全面的掌控。(是否是心里还有点小期待呢!)
最后,咱们简单看一下 Java 程序的执行过程,了解下它究竟是如何运行起来的。
这里的 Java 程序是文本格式的。好比下面这段 HelloXiaoli.java,它遵循的就是 Java 语言规范。其中,咱们调用的 System.out 等模块,就是 JRE 提供的类库。
经过 JDK 的工具 javac 进行编译后,就会产生 HelloWorld 的字节码。
1javac HelloXiaoli.java
复制代码
Java 字节码是沟通 JVM 和 Java 程序的桥梁,下面使用 javap 来看一下字节码到底长什么样子。javap基本使用
javap -verbose HelloXiaoli.class
复制代码
0 getstatic #2 <java/lang/System.out>
3 ldc #3 <Hello Xiaoli>
5 invokevirtual #4 <java/io/PrintStream.println>
8 return
复制代码
Java 虚拟机采用基于栈的架构(为何基于栈的架构详见:JVM 体系结构与工做方式),其指令由操做码和操做数组成。这些字节码指令,就叫作 opcode。其中,getstatic、ldc、invokeevirtual、return 等,就是 opcode。
咱们继续使用 hexdump 看一下字节码的二进制内容hexdump 命令
b2 00 02 12 03 b6 00 04 b1
复制代码
咱们能够看一下它们的对应关系。JVM 字节码对照表
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 会翻译这些字节码,它有两种执行方式:
.class 文件会被加载、存放到 metaspace 中,等待被调用,这里会有一个类加载器的概念。
JVM 的程序运行,都是在栈上完成的,这和其余普通程序的执行是相似的,分为堆和栈。好比咱们程序运行到了 main 方法,就会给它分配一个栈帧。当推出方法体时,会弹出相应的栈帧。其实,大多数字节码指令,就是不断的对栈帧进行操做。
而其它大块数据,是存放在堆上的。Java 在内存划分上会更为细致,关于这些概念,会在后面的章节中详细介绍。
咱们看下面的图,JVM 部分是咱们系列须要讲解的部分。
上面讲了这么多,让咱们再回头看看面试官提问的三个问题。
由于 Java 是一门抽象的语言,而且有自动内存管理机制。而操做系统没法去进行自动垃圾回收等操做,因此就有了虚拟机。虚拟机能够对字节码加载、自动垃圾回收、并发等。而 JVM 只是一个规范,定义了 .class 文件的结构、加载机制、数据存储、运行时栈等诸多内容,最经常使用的 JVM 实现就是 Hotspot。
JVM 的生命周期是和 Java 程序的运行同样,当程序运行结束,JVM 实例也就跟着消失了。具体的运行原理,会在后续文章中详细介绍,请关注小李哦!
Java 程序经过 javac 编译成 .class 文件,而后虚拟机将其加载到元数据区,执行引擎将会经过混合模式执行这些字节码。执行时,会翻译成操做系统相关的函数。
过程以下:Java 文件->编译器->字节码->JVM->机器码
本篇文章从三个角度了解了 JVM 在 Java 研发体系中的位置,并以一个简单的程序,看了下一个 Java 程序的执行过程。
咱们说的 JVM,狭义上指的就是 HotSpot。若是没有特殊说明,咱们都以 HotSpot 为准。
咱们知道 Java 之因此跨平台,就是因为 JVM 的存在。Java 的字节码,是沟通 Java 语言与 JVM 的桥梁,同时也是沟通 JVM 与操做系统的桥梁。
JVM 是一个很是小的集合,咱们常说的 Java 运行时环境,也就是 JRE 包含 JVM 和一部分基础类库。若是加上咱们经常使用的一些开发工具,就构成了整个 JDK。
Java 虚拟机栈采用基于栈的架构,有比较丰富的 opcode。这些字节码能够解释执行,也能够编译成机器码,运行在底层硬件上,能够说 JVM 是一种混合执行的策略。
留两道思考题给你们:
思考题我会在后面的章节为你们一一解答。
pc-shop.xiaoe-tech.com/appcCrwMYBx…