Java类加载机制

2021/6/12 20:24:18

本文主要是介绍Java类加载机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

JVM基础生命周期流程图在这里插入图片描述

classLoader.loadClass()的类加载(除引导类,所有类都一样)

  1. 加载:通过IO查找读取磁盘上的字节码文件,在调用到类才进行加载(调用类的方法,或者new 一个实例对象),会在内存创建一个Class对象,在方法区中代表这个类(用于获取该类的各种数据)。
  2. 验证:校验字节码文件是否符合规范。
  3. 准备:将类中的非最终静态变量分配内存地址,并赋予类型默认值(根据类型给予,与实际赋值无关)。最终静态变量会直接进行赋值操作,无需进行类型初始值赋予。
  4. 解析:将符号引用替换为直接引用,将一些静态方法(字符)替换为内存中的地址或句柄,即静态链接,而动态链接是指在程序运行期间将符号引用替换为直接引用。
  5. 初始化:对类的非最终静态变量初始化赋值(赋予类中指定的初始值,代替类型初始值),并且执行静态代码块。

注意:在未调用如何方法,未new对象的情况下。不会加载类,即只声明类如:

//不会加载类
User user=null;
//创建实例,会加载类
User user=new User();
//调用方法,会加载类
User.staticMethod();

能够获取到它的Calss对象,就会加载类

类加载器初始化

类的加载主要通过类加载器进行实现,而java包含的加载器如下:

  1. 引导类加载器(实例sun.misc.Launcher):由C++实现,用于加载支撑JVM运行的核心类库(Jre的lib目录下,如rt.jar、charsets.jar)
  2. 扩展类加载器(sun.misc.Launcher.ExtClassLoader):由Java实现,用于加载支撑JVM运行的ext扩展目录中的jar包(Jre的lib目录下ext目录)
  3. 应用程序类加载器(sun.misc.Launcher.AppClassLoader):由Java实现,用于加载ClassPath路径下的类包,也就是我们编程所写的类包。
  4. 自定义加载器:由Java实现,可以自己进行实现,常见的有Tomcat的加载器。
Launcher构造方法 父类加载器 应用类加载器 扩展类加载器

ExtClassLoader(扩展类加载器)在Launcher的构造方法中被设置为了AppClassLoader(应用类加载器)的父类加载器
而ExtClassLoader(扩展类加载器)的父类加载器由于是bootstrap(引导类加载器)而无法在Java中表示故被设置为null
另外bootstrap(引导类加载器)由C++进行加载,无需进行类加载

双亲委派机制

指类是如何判别在那个加载器中被加载

  • 寻找是否加载阶段
检查是否已加载,否则向上委托父加载器加载 向上委托 向上委托 引导类加载器 应用加载器 扩展类加载器
  • 进行加载阶段
父加载器加载失败由子加载器进行加载 向下委派 向下委派 应用加载器 引导类加载器 扩展类加载器

实际上其原理就是在AppClassLoader.loadClass方法中进行执行的,主要流程:

  1. 检查该类是否已经被本类加载器加载,已加载直接返回(被加载的对象)
  2. 诺未加载则判断是否存在父类加载器,存在即调用父类加载器的loadClass方法,诺无父类加载器(说明该加载器为引导类加载器),调用bootstrap类加载器加载.
  3. 诺到达bootstrap类加载器,并且bootstrap也未加载该类,则调用findClass方法尝试加载该类.
  4. 诺加载到该类,直接返回。未加载该类则进行向下委派,由子类加载器调用findClass方法进行尝试加载。

注意:findClass方法不是类加载器的方法而是类加载器父类URLClassLoader的方法,其功能为在类加载器路径里查找并加载该类

  • 双亲委派的作用
    1. 沙箱安全机制: 你自己写的同包名同类的java核心类时,双亲委派机制依旧会加载核心库中的类,而不会加载你所创建的类,从而保证核心类库不被随意篡改。
    2. 避免重复加载;当子类加载器加载过该类就无需向上委托。而当父类加载器加载了该类直接返回,而子类加载器无需加载,保证有且只有一个类加载器加载了该类,并且该类只被加载了一次。保证被加载类的唯一性

全盘委托机制(全盘负责委托机制)

  • 当ClassLoder(类加载器)加载一个类时,该类所依赖或引用的类也由该ClassLoder(类加载器)加载,除非指定使用另一个ClassLoder(类加载器)

实际上我学习的时候就有疑问,java核心类是最常被调用的类,那么如果每次都需要从应用类加载器从下到上找一次,会造成不必要的性能消耗。然而这个机制保证了不必要的消耗。

打破双亲委派机制

之所以要打破双亲委派机制,是为了实现不同的加载类的方式,例如Tomcat的war包就打破了双亲委派机制,从而实现运行不同的war包而不会互相干扰。

  • 基本要求
    • 自定义类加载器
    • 自定义类加载器重写loadClass方法(该方法中实现了双亲委派机制)
... 应用类加载器 Tomcat类加载器(加载Tomcat的lib) Tomcat类加载器<加载War1>WebappClassLoader Tomcat类加载器<加载War2>WebappClassLoader

注意:Tomcat类加载器打破了双亲加载机制,除无法加载的java核心类,都由Tomcat类加载器进行加载。

  • webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制
  • 因为打破双亲委派机制,即重写了findClass(),所以不会向父加载器寻找Class,即不会传递给父类。
  • webappClassLoader重写了loadClass()方法,不再遵循双亲委派,而是自己加载所有类(不包括java核心类)


这篇关于Java类加载机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程