Java类加载

类加载

Java虚拟机加载类的全过程包括,加载,验证,准备,解析和初始化。

  • 加载:根据路径找到对应的class文件,导入

  • 验证:确保class字节流中包含信息符合当前虚拟机要求,不会危害虚拟机安全。

  • 准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。-类变量(static)、不包含实例对象。

    public static value =123 ,设置初始值为0。

    public static final value =123 ,设置初始值为123。

  • 解析:虚拟机将常量区符号引用转换为直接引用(解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。)

  • 初始化:对静态变量和静态代码块执行初始化工作

何时触发初始化

  1. 使用new关键字实例化对象、读取或设置一个类的静态字段(final修饰、已经在编译期把结果放入常量区的静态字段除外)、已经调用一个类的静态方法时。
  2. 使用java.lang.reflect包的方法对类进行反射调用的时候。
  3. 当初始化一个类,如果它的父类没有进行初始化的时候,先出发父类的初始化。
  4. 当虚拟机启动时,用户需要指定一个要执行的子类(包含main方法的类),虚拟机会先初始化这个类。
  5. 当使用JDK1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例,对应类没有进行初始化,则先触发其初始化。

    对于静态字段,只有直接定义这个字段得类才会被初始化,通过子类引用父类定义得静态字段,只会出发父类得初始化。
    数组类不通过类加载器创建,是由虚拟机直接创建。
    一个类必须与类加载器一起确定唯一性。
    

类加载器:

通过类的全限定名来获取描述此类的二进制字节流,这个动作放在Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块就是“类加载器”。

  • Bootstrap ClassLoader(启动类加载器):负责加载系统类(jre/lib/rt.jar)

  • Extension ClassLoader(扩展类加载器):负责加载扩展类(jre/lib/ext/*.jar)

  • Applicaiton ClassLoader(应用程序类加载器):用于加载自己定义编写的类(classpath指定目录或jar中类)

  • User ClassLoader (用户自己实现的加载器)

    1
    比较两个类是否相等,只有这两个类是同一个类加载器加载的前提才有意义。

“双亲委派模型”:

从Java虚拟机角度来讲,只存在两个不通的类加载器,一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用的是C++语言实现,是虚拟机自身的一部分,另一种就是所有其他的加载器,是由Java语言实现,独立于虚拟机外部,并且全部继承自抽象类Java.lang.ClassLoader

工作过程

如果一类加载器收到了类加载请求,它首先不会自己尝试加载这个类,而是把请求委派给父类加载器去完成。

只有在父类反馈自己无法完成这个加载任务时,子类加载器才会尝试自己加载。

  1. 先检查需要加载的类是否已经被加载,如果没有被加载,则委托父加载器加载,父类继续检查,尝试请父类加载,这个过程是从下——-> 上;

  2. 如果走到顶层发现类没有被加载过,那么会从顶层开始往下逐层尝试加载,这个过程是从上 ——> 下;
    这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,但是在这种机制下这些系统的类已经被Bootstrap classLoader加载过了,所以并不会再去加载,从一定程度上防止了危险代码的植入。

    parents
    parents1

    双亲委派模型的破坏

JAVA热部署实现
首先谈一下何为热部署(hotswap),热部署是在不重启 Java 虚拟机的前提下,能自动侦测到 class 文件的变化,更新运行时 class 的行为。Java 类是通过 Java 虚拟机加载的,某个类的 class 文件在被 classloader 加载后,会生成对应的 Class 对象,之后就可以创建该类的实例。默认的虚拟机行为只会在启动时加载类,如果后期有一个类需要更新的话,单纯替换编译的 class 文件,Java 虚拟机是不会更新正在运行的 class。如果要实现热部署,最根本的方式是修改虚拟机的源代码,改变 classloader 的加载行为,使虚拟机能监听 class 文件的更新,重新加载 class 文件,这样的行为破坏性很大,为后续的 JVM 升级埋下了一个大坑。

另一种友好的方法是创建自己的 classloader 来加载需要监听的 class,这样就能控制类加载的时机,从而实现热部署。

热部署步骤:

1、销毁自定义classloader(被该加载器加载的class也会自动卸载);

2、更新class

3、使用新的ClassLoader去加载class

OSGi

Class回收

JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):

  • 类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
  • 加载该类的ClassLoader已经被GC。
  • 该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法

参考
https://www.cnblogs.com/lanxuezaipiao/p/4138511.html
https://juejin.im/post/57c66f386be3ff005851de05
https://juejin.im/post/5a1fad585188252ae93ab953
https://www.cnblogs.com/aspirant/p/7200523.html

-------------本文结束感谢您的阅读-------------
0%