初步了解Java的类加载机制

2021/9/13 22:05:20

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

Java运行时一个类是什么时候开始被加载的?

《java虚拟机规范》中并没有进行强制约束,交给了虚拟机自己去自由实现,HotSpot虚拟机是按需加载,在需要用到该类的时候加载这个类

1.Sun公司最早的Classic虚拟机
2.Sun/Oracle公司的HotSpot虚拟机
3.BEA公司的JRockit虚拟机
4.IBM公司的IBM J9虚拟机

https://docs.oracle.com/javase/8/docs/

image-20210911091426583

JVM一个类的加载过程?

一个类从加载到jvm内存,到从jvm内存卸载,它的整个生命周期会经历7个阶段:
1、加载( Loading)
2、验证( Verification)
3、准备( Preparation)
4、解析( Resolution)
5、初始化( Initialization)
6、使用( Using)
7、卸载( Unloading)
其中验证、准备、解析三个阶段统称为链接( Linking)

类加载生命周期

image-20210911092806235

加载: classpath、jar包、网络、某个磁盘位置下的类的dass二进制字节流读进来,在内存中生成一个代表这个类的 java.lang.Class对象放入堆上,class字节码二进制数据是放在元空间上,此阶段我们程序员可以干预,我们可以自定义类加载器来实现类的加载

-----------------------------------链接-------------------------------------
验证: 验证 Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证虚拟机的安全;

准备: 类变量赋默认初始值,int为0,long为0L, boolean为 false,引用类型为null,常量赋正式值

解析: 把符号引用翻译为直接引用
----------------------------------------------------------------------------
    
初始化: 当我们new一个类的对象,访问一个类的静态属性,修改一个类的静态属性,调用一个类的静态方法,用反射API对一个类进行调用,初始化当前类,其父类也会被初始化.那么这些都会触发类的初始化;

使用: 使用这个类

卸载:(其实很少会卸载类)
    1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例
    2.加载该类的Classloader已经被GC;
    3.该类的 java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法;
public class Test {

    // 常量,准备阶段 a 就赋值为123
    public static final int a = 123;

    // 静态类变量,准备阶段时b赋初始值为0 ,初始化阶段时赋值为234
    public static int b = 234;

    // 实例变量 ,准备阶段不处理,创建对象时赋值
    public int c = 789;

    //静态初始化块 初始化阶段执行
    static {
        System.out.println("=============静态初始化块===============");
        System.out.println("现阶段输出各种变量为: " + a + "," + b);
        System.out.println("======================================");
    }

    //初始化块  创建对象的时候执行
    {
        System.out.println("---------初始化块-----------------------");
        System.out.println("现阶段输出各种变量为: " + a + "," + b + "," + c);
        System.out.println("---------------------------------------");
    }

    //构造器  创建对象的时候执行
    public Test(){
        System.out.println("+++++++++++++构造器(构造函数)+++++++++++++");
        System.out.println("现阶段输出各种变量为: " + a + "," + b + "," + c);
        System.out.println("+++++++++++++++++++++++++++++++++++++++++");
    }


    public static void main(String[] args) {        
        //  -XX:+TraceClassLoading  (vm参数,用于监控类加载)
        new Test();
    }
}

一个类被初始化的过程

image-20210911092806235

类的初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码;进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,才真正初始化类变量和其他资源( 准备阶段:赋予变量初始化值 --> 初始化阶段:赋予变量真正的值)

继承时父子类的初始化顺序是怎样的?

案例代码:

public class ParentClass {

    //静态变量
    public static String p_StaticField = "父类--静态变量";

    //变量
    public String p_Field = "父类--变量";


    //静态初始化快
    static {
        System.out.println(p_StaticField);
        System.out.println("父类--静态初始化块");
    }

    //初始化块
    {
        System.out.println(p_Field);
        System.out.println("父类-初始化块");
    }

    public ParentClass() {
        System.out.println("父类--构造器");
    }

    public static void main(String[] args) {
        //new ParentClass();
    }

}

//-------------------------------------------------------------------------------------------------------

public class SonClass extends ParentClass {

    //静态变量
    public static String s_StaticField = "子类--静态变量";

    //变量
    public String s_Field = "子类--变量";

    //静态初始化快
    static {
        System.out.println(s_StaticField);
        System.out.println("子类--静态初始化块");
    }

    //初始化块
    {
        System.out.println(s_Field);
        System.out.println("子类--初始化块");
    }

    public SonClass() {
        System.out.println("子类--构造器");
    }

    public static void main(String[] args) {
        //new ParentClass();
        /*
        父类--静态变量
        父类--静态初始化块
        子类--静态变量
        子类--静态初始化块
        
        父类--变量
        父类-初始化块
        父类--构造器
        */
        
        new SonClass();
         /*
        父类--静态变量
        父类--静态初始化块
        子类--静态变量
        子类--静态初始化块
        
        父类--变量
        父类-初始化块
        父类--构造器
        子类--变量
        子类--初始化块
        子类--构造器
        */
    }
}

根据代码结果可知:

先加载父类静态变量以及静态初始块,然后是子类静态变量以及静态初始块,然后根据具体情况先加载父类变量以及初始化块,再加载子类变量以及初始化块

究竟什么是类加载器?

image-20210911092806235

类加载阶段,通过—个类的全限定名来获取描述该类的二进制字节流的这个动作的“代码”被称为“类加载器”( Class loader),这个动作是可以自定义实现的; 所以 类加载器其实也是程序代码

JVM有哪些类加载器?

站在Java虚拟机的角度来看,只存在两种不同的类加载器
1、启动类加载器( Bootstrap Classloader),使用C++语言实现,是虚拟机自身的部分
2、其他所有的类加载器,由Java语言实现,独立存在于虚拟机外部,并且大部分都继承自抽象类 java.lang.Classloader(父委派模式是通过组合来实现的)

站在Java开发者的角度来看,自JDK1.2开始,Java一直保持着三层类加载器架构;

image-20210911212510735

JVM中不同的类加载器加载哪些文件?

1、启动类加载器( Bootstrap ClassLoader)---------根的类加载器

<JAVA_HOME>\jre\lib目录下的 rt.jar 、resources.jar、 charsets. jar 被-Xbootclasspath参数所指定的路径中存放的类库

public class ClassLoader01 {

    public static void main(String[] args) {

        // 都是jre\lib目录下类,但是并不是所有类都通过BootstrapClassLoader加载
        System.out.println(HKSCS.class.getClassLoader());//BootstrapClassLoader->null
        System.out.println(BufferedInputStream.class.getClassLoader());//BootstrapClassLoader->null
        System.out.println(DefaultHandler.class.getClassLoader());//BootstrapClassLoader->null
        System.out.println(AppInitEvent.class.getClassLoader());//AppClassLoader
        
        /* (注意:底层是由c++写的,通过BootstrapClassLoader加载 结果为null)
        null
        null
        null
        sun.misc.Launcher$AppClassLoader@18b4aac2
        */

    }
}

2、扩展类加载器( Extension ClassLoader)

sun. misc. Launcher$ExtClassLoader

<JAVA_HOME>\jre\lib\ext , 被java.ext.dirs系统变量所指定的路径中所有的类库

3、应用程序类加载器( Application ClassLoader)---------系统的类加载器

sun. misc. Launcher$AppClassloader ,加载用户类路径( ClassPath)上所有的类库;



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


扫一扫关注最新编程教程