- 什么是虚拟机?
从字面意思上来看,顾名思义即使一台虚拟的计算机,用来执行虚拟的计算机指令,从大体上来看,虚拟机一般分为两种。一种是系统虚拟机,另外一种是程序虚拟机。
系统虚拟机:代表为VMware
程序虚拟机:代表为JVM(java虚拟机)
- JVM的作用?
负责将字节码加载到内存在中(运行时数据区)
将字节码转换为机器码,在各个系统中执行
负责存储数据
垃圾回收
- JVM的整体组成部分?
类加载
运行时数据区(堆、java虚拟机栈、方法区、程序计数器、本地方法栈)具体了解请点击(java运行时区域分布)
执行引擎(将字节码转换为机器码)
本地方法接口
垃圾回收
![](https://img-blog.csdnimg.cn/img_convert/793cb0f75b6618cb1fcbb57115df2882.png)
![](https://img-blog.csdnimg.cn/img_convert/1762cbc0435f46c78055cfceef3a884c.png)
程序将java代码首先转换为class文件(字节码),jvm通过类加载器用IO的方式将字节码加载到内存中(运行时数据区),但是字节码不能被底层系统所识别,所以需要特定的命令解析器(执行引擎)将字节码翻译成为底层系统指令交给CPU进行执行,在执行的过程中会调用到其他语言的接口(本地方法接口)来实现功能的实现。
- 类加载
- 类加载器的作用?
负责从硬盘|网络中加载节码信息
将节码信息加载到内存(运行时数据区)中
- 类加载过程?
- 加载
通过类名(地址)获取此类的二进制字节流
将这个字节流所代表的静态存储结构转换为方法区的运行结构
在内存中生成一个代表这个类的java.lang.Class对象,作为这个类的各种数据的访问接口
- 链接
验证:
对字节码文件的格式进行验证,文件是否被污染
对基本的语法格式进行验证
准备:
为静态的变量进行内存分配
静态常量在编译期间就初始化
解析:
将符号引用转为直接引用
将字节码的表现形式转为内存中的表现形式(内存地址)
初始化:
类的初始化,为类中的定义的静态变量进行赋值
3.初始化
为类中的定义的静态变量进行赋值
- 类什么时候会初始化?
在类中运行main方法
创建对象
使用类中的静态变量以及静态方法
反射Class.forNname("类的地址")
初始化子类的同时也会初始化其父类
注意:以下两种情况不会进行类初始化
先创建两个类
public class User {
static int a=10;
static final int b=20;
static{
System.out.println(a);
}
public class TestUser {
public static void main(String[] args) {
}
引用该类的静态常量
public class TestUser {
public static void main(String[] args) {
System.out.println(User.b);//调用类中的静态常量不会导致类的加载,有时会在常量在进行计算的情况下才会导致类的加载
}
![](https://img-blog.csdnimg.cn/img_convert/b588e6bd087060c591ac800b9e42fc75.png)
构造某一个类的数组时
public class TestUser {
public static void main(String[] args) {
User[] users=new User[10];//构造某个类的数组时不会导致该类的初始化
}
![](https://img-blog.csdnimg.cn/img_convert/aa5dc1828bad7e0cc04f6446eaefda63.png)
6.类加载器
引导类加载器 :用c/c++语言进行开发,java底层的开发语言,负责加载java核心类库
扩展类加载器 :加载jdk下的 jre/lib/ext 子目录下加载类库
![](https://img-blog.csdnimg.cn/img_convert/5ac4b83c97313b8a584390a5eae78821.png)
应用程序类加载器:加载程序中自己开发的类
ClassLoader classLoader=User.class.getClassLoader();
ClassLoader classLoader1=classLoader.getParent();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 程序类加载器
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1b6d3586 扩展类加载器
ClassLoader classLoader2=String.class.getClassLoader();
System.out.println(classLoader2);//null 启动类加载器
双亲委派机制:
我们以String类为例,创建一个java.lang.String的一个类
![](https://img-blog.csdnimg.cn/img_convert/f5a9967f3eaba2cb125c8dbe38c9929c.png)
public class String {
static{
System.out.println("自己创建的String类");
}
public static void main(String[] args) {
/*错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application*/
}
![](https://img-blog.csdnimg.cn/img_convert/d1a22bdecf2de40d8ecac057fc9fb47d.png)
我们在类中添加了一个main方法,按道理启动应该是没有什么大问题的,但是当我们运行之后,idea给我们报了一个错,说是在String这个类中找不到main方法,那发生这样的原因是什么呢
![](https://img-blog.csdnimg.cn/img_convert/23650524a05c96d2202c5e7933fd39e6.png)
原理:当一个类加载器收到类加载请求时,先会委托给父类加载器加载,如果父类加载器还有其父类,则继续向上委托,直到到根加载器(引导类加载器),如果还是找不到,则向下交给子类进行加载,一次类推,直到加载成功或者报错 ClassNotFoundException 异常。
7.本地方法接口
1.什么是本地方法接口?
用native关键字修饰的方法称为一个本地方法,没有方法体,例如:
![](https://img-blog.csdnimg.cn/img_convert/83e825f77de04627d9ee98d2640921ba.png)
![](https://img-blog.csdnimg.cn/img_convert/a35fa31852482b55fb3ab83342ad0747.png)
![](https://img-blog.csdnimg.cn/img_convert/7ea3b884ae17c7eb4f997e59355b3691.png)
![](https://img-blog.csdnimg.cn/img_convert/37c7e64fbc3b483efde8843fbf5e1c1c.png)
2.为什么用本地方法?
java语言需要与外部环境进行交互(例如需要访问内存,硬盘,其他的硬件设备),直接访问操作系统的接口即可。
8.执行引擎
1.作用
将加载到内存中的字节码(不能直接运行的机器码),解释/编译为不同平台的机器码。
![](https://img-blog.csdnimg.cn/img_convert/7e890d0adcc822f51b7f8c5b2f63c808.png)
2.为什么java是半编译半解释型语言?
解释器:将字节码逐行解释执行,效率低
编译器(JIT):将字节码编译,缓存起来,执行更高效,不会立即使用编译器
因为在java程序启动后,先使用解释器立即执行,省去了编译时间,程序运行一段时间后,对热点的编译缓存,提高了后续执行效率,故采用了解释器和编译器结合的方案