大家好,我是小趴菜,今天我们来复习一下JVM相关的知识
我们从开发一个程序到部署运行,代码的整体会经历如下流程
类加载
我们把一个java程序一般会打包成jar或者是war包放到对应的操作系统上运行,那么要能执行到我们程序主要还是依靠JVM虚拟机,但是JVM在执行我们的程序之前首先肯定是要加载我们的程序文件
所以在整个过程中又会经历如下几个阶段
加载阶段
我们将java程序编译后生成一个class文件,但是本质上它还是一个个的文件,所以会通过IO将class文件读取到内存当中
- 1:通过类的全限定名获取二进制字节流,就是读取整个文件的操作
- 2:将整个字节流的数据转化为内存结构
- 3:在内存中生成一个代表整个类的java.lang.Class对象
以上就是加载阶段要做的事情
链接
链接阶段又会分为三个阶段来执行
-
1:验证
因为JVM从外部读取文件,但是并不是任何文件都符合的,所以会有一个验证阶段,验证整个字节码文件是否符合
-
2:准备
1:为类的静态变量分配内存并设置默认值,即零值
2:不包含final的静态变量,其在编译的时候就会分配好默认值
这里的默认值并不是用户赋予的值,而是类型的默认值,比如int类型的默认值就是0,字符串类型的就是空
-
解析
主要将常量池内的符号引用转为直接引用的过程
初始化
主要是为类的静态变量赋予正确的初始值,也就是用户在程序中赋予的值,对类的变量进行初始化
类加载器
类加载器有以下几种类型
- 1:启动类加载器 null,因为底层是c语言实现,所以为null
- 2:扩展类加载器 sun.misc.Launcher$ExtClassLoader@7a46a697
- 3:系统类加载器 sun.misc.Launcher$AppClassLoader@18b4aac2
- 4:自定义类加载器
那么为什么需要这么多的类加载器,其实每个类加载器加载的类的路径是不一样的
启动类加载器加载的类路径如下
扩展类加载器加载的类路径如下
各个启动类加载器的关系
public Launcher() {
ExtClassLoader var1;
try {
//扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//系统类加载器
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
}
从源码中我们各个加载器之间是没有关系的,只不过加载系统类加载器的时候将扩展类加载器作为参数传递进去,但是并没有父子类相关的关系
双亲委派机制
现在需要初始化一个类,这时候系统类加载器会从扩展类加载器查询,看下能否加载这个类,而扩展类加载器并不会直接初始化,而是继续向启动类加载器委托,如果启动类加载器没有,那么再回到扩展类加载器加载,如果扩展类加载器没有,就要由系统类加载器进行加载了
其实整个过程就是个递归过程,类加载的源码如下
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//往上委托
c = parent.loadClass(name, false);
} else {
//从启动类加载器获取
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
c = findClass(name);
}
}
return c;
}
}
双亲委派机制有以下好处
- 1:安全,可以避免外部程序篡改
- 2:按需加载,比如String这个类,全程只需要加载一次就可以了
如何破坏双亲委派机制
- 1:自定义类加载器
Tomcat就实现了自定义类加载器,来保证多个服务之间类的加载不会相互影响,一个Tomcat是可以部署多个WEB应用的,如果这个两个应用出现了相同限定名的类,那么就需要都加载,并且两个是不一样的类
- 2:线程上下文类加载器
利用线程上下文类加载器,就比如JDBC