# JVM简介

优势：

1. write once run everywhere
2. 内存管理

## jvm空间加载运行

参考jvm规范：https://docs.oracle.com/javase/specs/index.html

- 

### 加载

![](img\class文件加载.png)

- 类加载器
  - Bootstrap：加载环境变量中$JAVA_HOME/jre/lib/rt.jar里所有的class，不是ClassLoader子类。
  - Extension：加载java平台中扩展功能的一些jar包。包括环境变量中$JAVA_HOME/jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包。
  - Application：加载classpath中指定的jar包及目录中class。
  - Custom自定义：应用程序根据自身需要自定义的ClassLoader，如tomcat、jboss都会根据j2ee规范自行是实现ClassLoader
- 双亲委派
  - 写类String写main方法无法启动，Bootstrap Class Loader 中已经加载了String，其中无main方法，无法启动。委派给父类，所有父类都加载不到才会有子类加载。不让jdk代码被修改。


### 链接

- 验证：字节码校验器会检查生成的字节码是否正确，如果验证失败会停止运行
- 准备：对于所有静态变量内存分配和默认值分配
- 识别：解析或识别是从运行时常量池的符号引用中动态具体的过程。

### 初始化

- 类或接口的初始化由执行类或接口初始化方法构成，这里所有的静态变量与原来的值将被指派，静态块将被执行。

### 线程空间

![](img\线程内存使用.png)

![](img\虚拟机栈.png)

- 线程共享
  - Heap堆：在虚拟机启动时创建，此内存区域的唯一目的就是存放对象实例，几乎所有的对象实例都在这里分配内存。存储数据不是线程安全的，<u>**堆和常量池**</u>空间不足时会触发OutOfMemoryError
  - MetaSpace元数据区：在1.8之后取代Method Area 方法区永久代，不在jvm中，使用本地内存，受操作系统内存限制。存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 线程隔离
  - VM Stack虚拟机栈：每个线程创建一个单独的运行时堆栈。对于每一个方法调用，一个称为栈内存栈帧被创建。所有局部变量将被创建在栈内存中。栈区域时线程安全的，因为他不是一个共享资源。
  - Navive Method Stack本地方法栈：与虚拟机所发挥的作用是非常相似的，其区别不过是虚拟机栈为虚拟机执行java方法（也就是字节码）服务，而本地方法栈则是为虚拟机使用到的Native方法服务。
  - Program Counter Register 程序计数器：每个线程必须分开程序计数器登记，当前执行的指令一旦被执行，程序计数器更新下一个指令。一块较小的内存空间。他的作用可以看做是当前线程所执行的字节码的行号指示器。

### 运行时数据区

- 程序计数器：指向当前线程正在执行的字节码指令的地址和行号

- 虚拟机栈：存储当前线程运行方法所需的数据、指令、返回地址(动态链接，注入使用查找实例)。

- - 栈帧：局部变量表，操作数栈、动态链接、出口
  - javap -c -v     .class   >    p.txt

- 本地方法 native

- 方法区   类信息、常量、静态变量、JIT（just in time）编译时信息（永久代对方法的实现）

- Heap  堆（内存模型）存放new出来的对象

- - 新生代
  - 老年代
  - 永久代/元数据区（方法区、非堆）

## 内存空间分配

![image-20211020152033799](img\image-20211020152033799.png)
