• 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() %>
    
  • 相关阅读:
    常用软件
    树和二叉树的一些基本术语
    二分查找(Binary Search)
    GPIO模拟IIC接口信号质量分析
    PAT (Basic Level) Practise:1021. 个位数统计
    PAT (Basic Level) Practise:1017. A除以B
    PAT (Basic Level) Practise:1027. 打印沙漏
    文件操作:输出文件二进制数据
    阈值与平滑处理
    图像基本操作
  • 原文地址:https://www.cnblogs.com/nm666/p/14489960.html
Copyright © 2020-2023  润新知