JVM
为什么学习 JVM
面试的需要
学过 Java 的程序员对 JVM 应该并不陌生。程序员为什么要学习 JVM 呢,其实不懂 JVM 也可以照样写出优质的代码,但是不懂 JVM 有可能别被面试官虐得体无完肤。
中高级程序员必备技能
项目管理,性能调优
虚拟机
所谓虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
大名鼎鼎的 VMware 就属于系统虚拟机,它是完全对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是 java虚拟机了,它专门为执行某个单个计算机程序而设计。在 java 虚拟机中执行的指令我们称为 java 字节码指令。
Java 虚拟机是一种执行 java 字节码文件的虚拟计算机,它拥有独立的运行机制。
Java 技术的核心就是 java 虚拟机,因为所有的 java 程序都运行在 java 虚拟机内部。
JVM 作用
Java 虚拟机负责装载字节码到其内部,解释/编译为对应平台上的机器码指令执行,每一条 java 指令,java 虚拟机中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪儿。
特点:
一次编译到处运行
自动内存管理
自动垃圾回收功能
现在的 JVM 不仅可以执行 java 字节码文件,还可以执行其他语言编译后的字节码文件,是一
个跨语言平台。
![](https://img-blog.csdnimg.cn/img_convert/2c4d04d89881b535bbd26600f9753b06.png)
JVM 整体组成部分
1.类加载器(ClassLoader)(负责加载字节码文件)
2.运行时数据区(Runtime Data Area)(存储运时数据,堆,java虚拟机栈(运行java自己的方法),方法区,程序计数器,本地方法栈)
3.执行引擎(Execution Engine)(更底层,把字节码翻译为机器码)
4.本地库接口(Native Interface)
5.垃圾回收
![](https://img-blog.csdnimg.cn/img_convert/96da37985734027ec5e75826deafe46b.png)
![](https://img-blog.csdnimg.cn/img_convert/4d5b42ab1fe3bb48ab8075c23cb0b2c8.png)
程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中的运行时数据区(Runtime Data Area) ,而字节码文件是 jvm 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由CPU 去执行,而这个过程中需要调用其他语言的接口 本地库接口(NativeInterface) 来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能。
而我们通常所说的 JVM 组成指的是运行时数据区(Runtime DataArea),因为通常需要程序员调试分析的区域就是“运行时数据区”,或者更具体的来说就是“运行时数据区”里面的 Heap(堆)模块。
JVM 结构-类加载
类加载子系统
![](https://img-blog.csdnimg.cn/img_convert/17d47a540eacf8b1283c4a5f85f8a357.png)
类加载器子系统负责从文件系统或者网络中加载 class 文件。 classLoader只负责 class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定。
加载的类信息存放于一块称为方法区的内存空间。
![](https://img-blog.csdnimg.cn/img_convert/0f65adba56089d3f3d276dc4dce59eb7.png)
class file 存在于硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载 JVM 当中来,根据这个模板实例化出 n 个实例.
class file 加载到 JVM 中,被称为 DNA 元数据模板.
此过程就要有一个运输工具(类加载器 Class Loader),扮演一个快递员的角色.
类加载过程
![](https://img-blog.csdnimg.cn/img_convert/e57497936d3d2f72ad78636bf799c5ec.png)
加载
1. 通过类名(地址)获取此类的二进制字节流.
2. 将这个字节流所代表的静态存储结构转换为方法区(元空间)的运行时结构.3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为这个类的各种数据的访问入口。
链接
验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;
验证文件格式是否一致: class 文件在文件开头有特定的文件标识(字节码文件都以 CA FE BA BE 标识开头);主,次版本号是否在当前 java 虚拟机接收范围内.
元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java 语言规范的要求,例如这个类是否有父类;是否继承浏览不允许被继承的类(final 修饰的类).....
准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值;
不包含用 final 修饰的 static 常量,在编译时进行初始化.
例如: public static int value = 123;
value 在准备阶段后的初始值是 0,而不是 123.
解析:将类的二进制数据中的符号引用替换成直接引用(符号引用是 Class 文件的逻辑符号,直接引用指向的方法区中某一个地址).
初始化
初始化,为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。初始化阶段就是执行底层类构造器方法<clinit>()的过程。
此方法不需要定义,是 javac 编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来的。
类什么时候初始化?
JVM 规定,每个类或者接口被首次主动使用时才对其进行初始化.
通过 new 关键字创建对象
访问类的静态变量,包括读取和更新
访问类的静态方法
对某个类进行反射操作
初始化子类会导致父类的的初始化
执行该类的 main 函数
其实除了上面的几种主动使用,以下两种情况为被动使用不会加载类.
1.引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致类加载,比如:
public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
public final static int RANDOM = new Random().nextInt() ; //会导致类加载
2.构造某个类的数组时不会导致该类的初始化,比如:
Student[] students = new Student[10] ;
类的初始化顺序
对 static 修饰的变量或语句块进行赋值.如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
顺序是:父类 static –> 子类 static