教妹学Java(七):究竟什么是JVM?-阿里云开发者社区

开发者社区> 沉默王二> 正文

教妹学Java(七):究竟什么是JVM?

简介: 教妹学Java(七):究竟什么是JVM?
+关注继续查看

“二哥,最近疫情闹得人心惶惶,无心学习了,怎么办?”


“三妹啊,你要知道,历史上经历过无数次的动荡与不安,但最后,都挺过去了。有人破坏,有人修复。安安心心学习,疫情过后肯定能够派上大用场,到时候经济复苏,人才亟需,懂吗?”


“二哥,说话就是不一样,一句话就安抚了我不安的心情。”


“那还不开始今天的主题?”


“二哥,上一篇文章中你给我解释了什么是 JDK,JRE 和 JVM,但我想知道 JVM 究竟是什么,它能干什么事。”


“三妹啊,搬个凳子坐我旁边,听二哥来给你慢慢说啊。”


01、什么是 JVM


再来回顾一下。JVM(Java Virtual Machine)俗称 Java 虚拟机。之所以称为虚拟机,是因为它实际上并不存在。它提供了一种运行环境,可供 Java 字节码在上面运行。


02、JVM 能做什么


JVM 提供了以下操作:


加载字节码

验证字节码

执行字节码

提供运行时环境

JVM 定义了以下内容:


存储区

类文件格式

寄存器组

垃圾回收堆

致命错误报告等

03、JVM 的内部结构


我们来尝试理解一下 JVM 的内部结构,它包含了类加载器(Class Loader)、运行时数据区(Runtime Data Areas)和执行引擎(Excution Engine)。


aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTc5Mzg5LWUxMjM5ODg4NTEzYjk5YmYucG5n.png


1)类加载器


类加载器是 JVM 的一个子系统,用于加载类文件。每当我们运行一个 Java 程序,它都会由类加载器首先加载。Java 中有三个内置的类加载器:


启动类加载器(Bootstrap Class-Loader),加载 jre/lib 包下面的 jar 文件,比如说常见的 rt.jar(包含了 Java 标准库下的所有类文件,比如说 java.lang 包下的类,java.net 包下的类,java.util 包下的类,java.io 包下的类,java.sql 包下的类)。


扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。


应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。


一般来说,Java 程序员并不需要直接同类加载器进行交互。JVM 默认的行为就已经足够满足大多数情况的需求了。不过,如果遇到了需要和类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就不得不花大量的时间去调试

ClassNotFoundException 和 NoClassDefFoundError 等异常。


对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等(比如两个类的 Class 对象不 equals)。


三妹啊,是不是有点晕,来来来,通过一段简单的代码了解下。


public class Test {
    public static void main(String[] args) {
  ClassLoader loader = Test.class.getClassLoader();
  while (loader != null) {
    System.out.println(loader.toString());
    loader = loader.getParent();
  }
    }
}


每个 Java 类都维护着一个指向定义它的类加载器的引用,通过 类名.class.getClassLoader() 可以获取到此引用;然后通过 loader.getParent() 可以获取类加载器的上层类加载器。


上面这段代码的输出结果如下:


sun.misc.Launcher$AppClassLoader@18b4aac2

sun.misc.Launcher$ExtClassLoader@4617c264



第一行输出为 Test 的类加载器,即应用类加载器,它是 sun.misc.Launcher$AppClassLoader 类的实例;第二行输出为扩展类加载器,是 sun.misc.Launcher$ExtClassLoader 类的实例。那启动类加载器呢?


按理说,扩展类加载器的上层类加载器是启动类加载器,但在我这个版本的 JDK 中, 扩展类加载器的 getParent() 返回 null。所以没有输出。


2)运行时数据区


运行时数据区又包含以下内容。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTc5Mzg5LTc2ODgxZjRlODdjYWVkZWUucG5n.png



PC寄存器(PC Register),也叫程序计数器(Program Counter Register),是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。


JVM 栈(Java Virtual Machine Stack),与 PC 寄存器一样,JVM 栈也是线程私有的。每一个 JVM 线程都有自己的 JVM 栈,这个栈与线程同时创建,它的生命周期与线程相同。


本地方法栈(Native Method Stack),JVM 可能会使用到传统的栈来支持 Native 方法(使用 Java 语言以外的其它语言[C语言]编写的方法)的执行,这个栈就是本地方法栈。


堆(Heap),在 JVM 中,堆是可供各条线程共享的运行时内存区域,也是供所有类实例和数据对象分配内存的区域。


方法区(Method area),在 JVM 中,被加载类型的信息都保存在方法区中。包括类型信息(Type Information)和方法列表(Method Tables)。方法区是所有线程共享的,所以访问方法区信息的方法必须是线程安全的。


运行时常量池(Runtime Constant Pool),运行时常量池是每一个类或接口的常量池在运行时的表现形式,它包括了编译器可知的数值字面量,以及运行期解析后才能获得的方法或字段的引用。简而言之,当一个方法或者变量被引用时,JVM 通过运行时常量区来查找方法或者变量在内存里的实际地址。


3)执行引擎


执行引擎包含了:


解释器:读取字节码流,然后执行指令。因为它一条一条地解释和执行指令,所以它可以很快地解释字节码,但是执行起来会比较慢。


即时(Just-In-Time,JIT)编译器:即时编译器用来弥补解释器的缺点,提高性能。执行引擎首先按照解释执行的方式来执行,然后在合适的时候,即时编译器把整段字节码编译成本地代码。然后,执行引擎就没有必要再去解释执行方法了,它可以直接通过本地代码去执行。执行本地代码比一条一条进行解释执行的速度快很多。编译后的代码可以执行的很快,因为本地代码是保存在缓存里的。


04、鸣谢


本篇文章为《教妹学Java》专栏的第七篇文章,是不是有趣得很?我相信你能感受的到,这可是全网独一份,我看到已经有人在模仿了。现在定价只需 9.9 元,9.9 元你连一杯奶茶都买不到,但却能买下二哥精心制作的专栏,据说 CSDN 已经考虑涨价了,毕竟已经卖出一百多份了。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Internet 与万维网|学习笔记
快速学习 Internet 与万维网
6 0
HashMap 的产生与原理
数组:一片物理上连续的大小确定的储存空间。 好处:根据下标快速的查找和修改里面的内容。 缺点:大小确定,无法修改。添加新的元素或者删除元素比较麻烦。
5 0
从 GFS 失败的架构设计来看一致性的重要性(4)
从 GFS 失败的架构设计来看一致性的重要性(4)
4 0
使用nginx split_clients实现A/B测试
A/B测试是微服务时代一个非常重要的能力,需求快速迭代上线和发布,必须具备对应的灰度测试的能力,对于上线的内容进行隔离验证,减少频繁变更带来的风险。本文介绍如何使用nginx 实现前端和后端服务A/B测试。
3 0
ebay增强可用性的4个原则(3)
ebay增强可用性的4个原则(3)
1 0
服务器相关知识|学习笔记
快速学习服务器相关知识
6 0
计算机网络的概述|学习笔记
快速学习计算机网络的概述
4 0
+关注
沉默王二
微信搜索「沉默王二」,回复关键字「00」获取硬核计算机基础资料。
1084
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载