|
2 | 2 |
|
3 | 3 | - [前言](#前言) |
4 | 4 | - [核心知识](#核心知识) |
| 5 | + - [JVM体系结构](#jvm体系结构) |
| 6 | + - [JVM各个模块简介](#jvm各个模块简介) |
| 7 | + - [JVM是如何工作的?](#jvm是如何工作的) |
5 | 8 | - [1. 运行时数据区域](#1-运行时数据区域) |
6 | 9 | - [1. 程序计数器(线程私有)](#1-程序计数器线程私有) |
7 | 10 | - [2. 虚拟机栈(线程私有)](#2-虚拟机栈线程私有) |
|
85 | 88 | - [更新说明](#更新说明) |
86 | 89 |
|
87 | 90 | <!-- /TOC --> |
88 | | - |
89 | 91 | # 前言 |
90 | 92 |
|
91 | 93 | 在本文将深入讨论 Java 虚拟机相关核心知识 |
|
108 | 110 |
|
109 | 111 | # 核心知识 |
110 | 112 |
|
| 113 | +- JVM 基本结构 |
| 114 | + - 类加载器 |
| 115 | + - 执行引擎 |
| 116 | + - 运行时数据区 |
| 117 | + - 本地接口 |
| 118 | + |
| 119 | +Class Files -> ClassLoader -> 运行时数据区 -> 执行引擎,本地库接口 -> 本地方法库 |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | +## JVM体系结构 |
| 126 | + |
| 127 | +虚拟机是物理机器的软件实现。Java 的开发遵循 write once run anywhere(一次编写到处乱跑)理念,它运行在VM(虚拟机)上。编译器将 Java 文件编译成 Java.class 文件,之后,将 .class 文件输入到 JVM 中,加载并执行该类文件。下图是JVM 的体系结构 |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | +### JVM各个模块简介 |
| 134 | + |
| 135 | +1. 运行时数据区:经过编译生成的字节码文件(class文件),由 class loader(类加载子系统)加载后交给执行引擎执行。在执行引擎执行的过程中产生的数据会存储在一块内存区域。这块内存区域就是运行时区域 |
| 136 | + |
| 137 | +2. 程序计数器:用于记录当前线程的正在执行的字节码指令位置。由于虚拟机的多线程是切换线程并分配 cpu 执行时间的方式实现的,不同线程的执行位置都需要记录下来,因此程序计数器是线程私有的 |
| 138 | + |
| 139 | +3. 虚拟机栈:虚拟机栈是 Java 方法执行的内存结构,虚拟机会在每个 Java 方法执行时创建一个“栈桢”,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。当方法执行完毕时,该栈桢会从虚拟机栈中出栈。其中局部变量表包含基本数据类型和对象引用 |
| 140 | + - 在 Java 虚拟机规范中,对这个区域规定了两种异常状态:如果线程请求的栈的深度大于虚拟机允许的深度,将抛出 StackOverFlowError 异常(栈溢出),如果虚拟机栈可以动态扩展(现在大部分 Java 虚拟机都可以动态扩展,只不过 Java 虚拟机规范中也允许固定长度的 Java 虚拟机栈),如果扩展时无法申请到足够的内存空间,就会抛出 OutOfmMemoryError 异常(没有足够的内存) |
| 141 | + |
| 142 | +4. 本地方法栈:类似 Java 方法的执行有虚拟机栈,本地方法的执行则对应有本地方法栈 |
| 143 | + |
| 144 | +5. 方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。线程共享(看存储的数据就知道了) |
| 145 | + |
| 146 | +6. Java 堆(Heap):堆的主要作用是存放程序运行过程中创建的对象实例,因为要存放的对象实例有可能会极多,因此也是虚拟机内存管理中最大的一块。并且由于硬件条件有限,所以需要不断回收已“无用”的实例对象来腾出空间给新生成的实例对象;因此 Java 的垃圾回收主要是针对堆进行回收的(还有方法区的常量池),Java 堆很多时候也被称为GC堆(Garbage Collected Heap)。 |
| 147 | + |
| 148 | +7. 类加载机制(Class Loader):类加载子系统是根据一个类的全限定名来加载该类的二进制流到内存中,在JVM 中将形成一份描述 Class 结构的元信息对象(方法区),通过该元信息对象可以获知 Class 的结构信息:如构造函数,属性和方法等,Java 允许用户借由这个 Class 相关的元信息对象间接调用 Class 对象的功能。 |
| 149 | + |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | +### JVM是如何工作的? |
| 155 | + |
| 156 | +如上面的体系结构图所示,JVM 分为三个主要的子系统: |
| 157 | + |
| 158 | +1. 类加载器子系统 |
| 159 | +2. 运行时数据区 |
| 160 | +3. 执行引擎 |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | +**1、类加载器子系统** |
| 165 | + |
| 166 | +Java的动态类加载功能是由类加载器子系统处理的。它负责加载、链接,并且在**运行时**首次引用类的时候初始化类,而不是在编译期间。 |
| 167 | + |
| 168 | +**1.1、加载** |
| 169 | + |
| 170 | +这个组件负责加载类。BootStrap类加载器、Extension类加载器和Application类加载器是实现这个功能的三大类加载器。 |
| 171 | + |
| 172 | +1. **BootStrap类加载器** —— 负责从classpath加载类,如果没有类存在,将只加载**rt.jar**。这个加载器的优先级最高。 |
| 173 | +2. **Extension类加载器** —— 负责加载**扩展文件夹(jre\lib)**中的类。 |
| 174 | +3. **Application类加载器** —— 负责加载**应用级classpath**和环境变量指向的路径下的类。 |
| 175 | + |
| 176 | +上述**类加载器**在加载类文件时遵循**委托层次结构算法**。 |
| 177 | + |
| 178 | +**1.2、链接** |
| 179 | + |
| 180 | +1. **校验** —— 字节码验证器将校验生成的字节码是否正确,如果校验失败,我们将获得**校验错误信息**。 |
| 181 | +2. **准备** —— 对于所有的静态变量,内存将被申请并分配默认值。 |
| 182 | +3. **解析** —— 所有**标记的内存引用**从**方法区域**被替换成的**原始引用**。 |
| 183 | + |
| 184 | +**1.3、初始化** |
| 185 | + |
| 186 | +这是类加载的最后阶段,所有的静态变量都将被分配原值,静态代码块将被执行。 |
| 187 | + |
| 188 | + |
| 189 | + |
| 190 | +运行时数据区 |
| 191 | + |
| 192 | +运行时数据区被划分为五个主要部分: |
| 193 | + |
| 194 | +- **方法区** —— 所有**类级数据**都将存储在这里,包括**静态变量**。每一个JVM只有一个方法区,并且它是一个共享资源。 |
| 195 | +- **堆区** —— 所有**对象**及其对应的**实例变量**和**数组**等存储在此,每个JVM同样只有一个堆区。由于**方法区**和**堆区**是多线程内存共享,因此存储的数据是非线程安全的。 |
| 196 | +- **栈区** —— 每个线程都会创建一个单独的**运行时栈**。在每一次**方法调用**,都会在栈内存中创建一个**栈帧(Stack Frame)**。所有**局部变量**将在栈内存中创建。栈区是线程安全的,因为它不是一个共享资源。栈帧可以被划分为三个实体: |
| 197 | + |
| 198 | +> **局部变量数组** —— 与方法中有多少局部变量有关,相应的值将存储在此处。 |
| 199 | +> **操作数栈** —— 如果任何的中间操作需要被执行,**操作数栈**将作为运行时工作区来执行操作。 |
| 200 | +> **帧数据** —— 与方法相对应的所有符号存储在此。在任何异常情况下,catch块的信息被保留在帧数据中。 |
| 201 | +
|
| 202 | +- **PC寄存器** —— 每一个线程都有单独的**PC寄存器**,一旦执行指令,PC寄存器将被下一条指令**更新**,保存当前**执行指令**的地址。 |
| 203 | +- **本地方法栈** —— 本地方法栈保存本地方法信息,每一个线程都会创建一个单独的本地方栈。 |
| 204 | + |
| 205 | + |
| 206 | + |
| 207 | +**3、执行引擎** |
| 208 | + |
| 209 | +分配到运行时数据区的字节码将被执行引擎执行。执行引擎读取字节码并逐一执行。 |
| 210 | + |
| 211 | +- **解释器** —— 解释器能更加快速地解释字节码,但是执行缓慢。解释器的缺点是当多次调用一个方法时,每次都要重新解释。 |
| 212 | +- **JIT编译器** —— JIT编译器弥补了解释器的不足。执行引擎使用解释器来转换字节码,当它发现重复的代码时,它将使用JIT编译器来编译整个字节码并转换为本地代码。本地代码将直接被重复的方法所调用,从而提高系统性能。 |
| 213 | +- **中间代码生成器** —— 生成中间代码。 |
| 214 | +- **代码优化器** —— 负责优化上述生成的中间代码。 |
| 215 | +- **目标代码生成器** —— 负责生成机器码或者本地代码。 |
| 216 | +- **分析器** —— 一个特殊的组件,负责查找热点代码,比如一个方法是否被调用多次。 |
| 217 | +- **垃圾回收器** —— 回收并删除未引用的对象。可以通过调用**System.gc()**来触发垃圾回收,但不能保证它执行。JVM的垃圾回收是回收被创建的对象。 |
| 218 | + |
| 219 | +**Java本地接口(JNI)**:**JNI**与**本地方法库**交互,并为执行引擎提供**本地方法库**。 |
| 220 | + |
| 221 | +**本地方法库(Native Method Libraries)**:它是执行引擎所需的本地库集合。 |
| 222 | + |
| 223 | + |
| 224 | + |
| 225 | + |
| 226 | + |
111 | 227 | ## 1. 运行时数据区域 |
112 | 228 |
|
113 | 229 | <div align="center"> <img src="assets/540631a4-6018-40a5-aed7-081e2eeeaeea.png" width="500"/> </div><br> |
@@ -856,6 +972,10 @@ public static final int value = 123; |
856 | 972 |
|
857 | 973 | 初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段即虚拟机执行类构造器 \<clinit>() 方法的过程。 |
858 | 974 |
|
| 975 | + |
| 976 | + |
| 977 | + |
| 978 | + |
859 | 979 | 在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。 |
860 | 980 |
|
861 | 981 | \<clinit>() 方法具有以下特点: |
@@ -952,6 +1072,9 @@ public static void main(String[] args) { |
952 | 1072 |
|
953 | 1073 | #### 3. 双亲委派模型 |
954 | 1074 |
|
| 1075 | +- 为什么要使用双亲委派模型? |
| 1076 | + - 主要是为了避免重复加载的问题 |
| 1077 | + |
955 | 1078 | JVM 如何加载一个类的过程,双亲委派模型中有哪些方法有没有可能父类加载器和子类加载器,加载同一个类?如果加载同一个类,该使用哪一个类? |
956 | 1079 |
|
957 | 1080 | - **双亲委派机制图** |
@@ -1225,7 +1348,13 @@ for (int i = 1; i < 100; i++) { |
1225 | 1348 | - [咕泡学院-James老师_腾讯课堂](https://ke.qq.com/teacher/2904270631) |
1226 | 1349 | - [Java虚拟机概述和基本概念](https://centmeng.github.io/2017/03/30/Java%E6%9E%B6%E6%9E%84%E5%B8%88-JVM/) |
1227 | 1350 | - [JVM性能监控工具 - 简书](https://www.jianshu.com/p/25e94a1399a0) |
1228 | | - |
| 1351 | + |
| 1352 | +- [探索JVM底层奥秘ClassLoader源码分析与案例讲解_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili](https://www.bilibili.com/video/av17748750?from=search&seid=7485294466500539956) |
| 1353 | + |
| 1354 | +- [一个“Hello World”理解JVM运行时数据区 - 西楼有酒 - 博客园](https://www.cnblogs.com/JunFengChan/p/9250585.html) |
| 1355 | + |
| 1356 | +- [JVM体系结构讲解](https://zhuanlan.zhihu.com/p/28347393) |
| 1357 | + |
1229 | 1358 |
|
1230 | 1359 |
|
1231 | 1360 |
|
|
0 commit comments