Java类的加载机制

厨房有只偷吃的猫的Java高级开发之路~

声明:部分图片来源网络

一、类生命周期

首先来看一下类的生命周期:

二、类加载器(常见3种)

类加载器负责装入类,搜索网络、jar、zip、文件夹、二进制数据、内存指定位置的类资源。一个Java程序运行,至少有三个类加载器示例,负责不同类的加载。

三、问题

  • 如何查看类对应的加载器?
  • JVM如何知道我们的类在何方?
  • 类会不会重复加载?
  • 静态属性、静态代码块什么时候加载?
  • 类是怎样卸载的?
  • 双亲委派模型是什么?

3.1 查看类对应的加载器

我们可以通过JDK-API进行查看:java.lang.Class.getClassLoader()

该方法会返回装载类的类加载器

如果这个类是由bootstrapClassLoader加载的,那么这个方法在这种实现中返回null。

测试代码如下:

public class ClassLoaderView {
    public static void main(String[] args) throws Exception {

        //加载核心类库的 BootStrap ClassLoader
        System.out.println("核心类加载器:"+
                ClassLoaderView.class.getClassLoader()
                        .loadClass("java.lang.String").getClassLoader());
        
        //加载拓展库的 Extension ClassLoader
        System.out.println("扩展类库加载器:"+
                ClassLoaderView.class.getClassLoader()
                        .loadClass("com.sun.nio.zipfs.ZipCoder").getClassLoader());
        
        //加载应用程序的
        System.out.println("应用程序库加载器:"+
                ClassLoaderView.class.getClassLoader());
        
        //双亲委派模型 Parents Delegation Model
        System.out.println("应用程序库加载器的父类:"+
                ClassLoaderView.class.getClassLoader()
                        .getParent());
        
        System.out.println("应用程序库加载器的父类的父类:"+
                ClassLoaderView.class.getClassLoader()
                        .getParent().getParent());

    }
}

打印出来的结果:

核心类加载器:null
扩展类库加载器:sun.misc.Launcher$ExtClassLoader@3a4afd8d
应用程序库加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
应用程序库加载器的父类:sun.misc.Launcher$ExtClassLoader@3a4afd8d
应用程序库加载器的父类的父类:null

分析:

查看打印结果发现String类被核心类加载器加载,返回的是null;

ZipCoder类被扩展类库加载器加载,返回ExtClassLoader;

String所在的java.lang包在rt.jar包的目录下,而我们测试扩展类库加载器所用的ZipCoder类在zipfs.jar包目录下。去Java安装目录查看,发现rt.jar包在/jre/lib目录下,zipfs.jar在/jre/lib/ext目录下。

ClassLoaderView类被应用程序库加载器加载,返回AppClassLoader;

3.2 JVM如何知道类在哪里呢?

我们的class信息存放在不同的位置,比如桌面上的jar、项目中的bin、src、target目录等等,那JVM是如何找到类的位置呢?

实际上,我们通过查看jdk源码sun.misc.Launcher.AppClassLoader类,如下图所示,可以发现它读取了java.class.path配置,去指定的地址加载类资源了。

验证:

利用jps、jcmd两个命令

  1. jps查看本机Java进程
  2. 查看运行时配置:jcmd 进程号 VM.system_properties

按照,以上信息,我们查看nacos-server.jar的信息:jcmd 343240 VM.system_properties

找到java.class.path发现nacos-server.jar的路径

3.3 类不会重复加载

类的唯一性:同一个类加载器,类名一样,代表是同一个类。

3.4 静态属性、静态代码块什么时候加载?

静态属性、静态代码块,是在类加载后,第一次使用对象的时候加载

3.5 类的卸载

类什么时候会被卸载?

类的卸载需要满足两个条件:

  1. 该Class所有的实例都已经被GC;
  2. 加载该类的ClassLoader实例已经被GC;

验证方式:

  • JVM启动中增加 -verbose:class 参数,输出类加载和卸载的日志信息。

3.6 双亲委派模型

为了避免重复加载,由上到下逐级委托,由上到下逐级查找。如下图:

最低端的子类加载器首先不会自己去尝试加载类,而是把这个请求委派给父加载器去完成;

每一层次的加载器都是如此,因此所有的类加载请求都会传给上层的启动类加载器。

只有当父加载器反馈自己无法完成该加载请求(也就是,该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

参考3.1中查看类加载器的代码。

3.7 热加载功能的理解

很多框架中的热加载功能实际上是创建了一个新的classloader

比如:

tomcat容器,当他检查出jsp发生变化,就行创建一个新的类加载器去加载jsp文件,在jsp文件中添加如下代码,会发现每次更新完jsp返回的classloader的id是不一样的。

<% this.class.getClassLoader() %>
原文地址:https://www.cnblogs.com/nm666/p/14489960.html