JVM类加载

本文详细介绍了Java的类加载过程,包括Bootstrap、Extension和Application ClassLoader的工作原理,以及类的验证、准备、解析和初始化阶段。强调了双亲委派模型在类加载中的作用,并通过实例展示了类初始化的顺序。同时还探讨了类加载的触发条件及卸载情况。
摘要由CSDN通过智能技术生成

Native Method 就是本地方法

类字节码

![未命名文件 (3).png](https://img-blog.csdnimg.cn/img_convert/f5a7a5739a374cd0e51286c1a9efa0d1.png#clientId=u49fc9ce4-6e48-4&crop=0&crop=0&crop=1&crop=1&from=drop&height=342&id=u6fe92853&margin=[object Object]&name=未命名文件 (3).png&originHeight=331&originWidth=668&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21935&status=done&style=none&taskId=u96590149-2be6-41b4-9cb6-ef3c261c7a9&title=&width=690)
计算机不能直接运行java代码

Java内存结构

![JVM内存.png](https://img-blog.csdnimg.cn/img_convert/9c7529b91eeb814efb1fa1e628013269.png#clientId=u8611501d-4645-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u0d3d3e03&margin=[object Object]&name=JVM内存.png&originHeight=1823&originWidth=1989&originalType=binary&ratio=1&rotation=0&showTitle=false&size=262489&status=done&style=none&taskId=uf453dc2f-7e5e-4826-9cfc-40f20a1faac&title=)

类加载器

**Bootstrap ClassLoader (引导程序加载器/启动类加载器) **
最顶层的类加载器使用,使用C++写的,是所有加载类的父类
负责%JAVA_HOME%/lib
Extension ClassLoader (扩展类加载器)
Java实现, %JAVA_HOME%/lib/ext,或者所有背java.ext.dirs系统变量所指定的路径下的jar包
AppClassLoader(应用加载器Applaction ClassLoader
用户加载器,负责加载当前应用classpath下所有jar包和类

public class Main{
    public static void main(String[] args) {
        System.out.println(Main.class.getClassLoader());
        System.out.println(Main.class.getClassLoader().getParent());
        System.out.println(Main.class.getClassLoader().getParent().getParent());
    }
}

// 
sun.misc.Launcher$AppClassLoader@18b4aac2  //应用加载器
sun.misc.Launcher$ExtClassLoader@330bedb4  // 扩展类加载器
null //null是BootStrap ClassLoader 

Linking

Verify(验证)保证被加载的类的正确性

  - 文件格式验证:验证字节流是不是符合Class文件格式的规范
  - 元数据验证:对字节码描述的信息进行语义分析,确定描述的信息符合Java语言规范,比如这个类是不是有父类?
  - 字节码验证:通过数据流和控制流分析,确定语义是合法的,符合逻辑的
  - 符号引用验证:确保解析动作可以正确的执行

Perpare(准备):为类的静态变量分配内存,并将其初始化为默认值

  - 此时内存分配的仅为类变量(static),不包括实例变量。对象实例化的时候,会随着变量初始化的时候被分配到堆中
  - 此时的初始值,通常是数据类型的默认值(0,0L,false,null),不是java代码中显式被赋予的值
public static int a = 3;
//在Linking过程中被初始化的值是a = 0;
// 把值初始化为3在初始化的时候才会执行

Resolve(解决,解析):把类中的符号引用转换为直接引用。
虚拟机将常量池的符号引用替换为直接引用的过程,解析动作主要正对类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点
**符号引用:**一组符号来描述目标,可以是任何字面量。
**直接引用:**直接指向目标的指针,相对偏移量或一个间接定位到目标的句柄。???

https://blog.csdn.net/luzhensmart/article/details/82627897
注意:
对于被static和final修饰的常量必须显示赋值

package com.chaossnow;

import com.chaossnow.absclass.Human;

/**
 * @program: findwordcode
 * @description:
 * @author: chaos
 * @create: 2022-09-11 15:18
 **/
public class InitializedTest{
    public int value; //默认0
    public static int staticValue;//默认0
    public static Human chaos;
    public Human chao;
    public int[] nums;
    public int[] ints = new int[6];

    //ConstantValue 在编译的时候就会根据常量的设置,将CONSTANT_VALUE赋值为3,会在编译期将其结果放入调用它的类的常量池中
    public static final int CONSTANT_VALUE = 3;
    //对于同时被static和final,必须要显示赋值
    public static final int SUCCEESS_HEAD = 1;

    public static void main(String[] args) {
        InitializedTest initializedTest = new InitializedTest();
        initializedTest.dome();

    }


    public static void say(){
        System.out.println("我是初始化");
    }
    public void dome(){
        String name;

        // System.out.println("我是: "+ name);对于局部变量必须在初始化之间就显示赋值,编译不通过
        System.out.println("我是普通全局基本变量:" + value);//0
        System.out.println("我是静态全局基本变量: "+ staticValue);//0
        //对于引用数据对象,在没有显示指定实例化对象的时候,会赋予默认的零值:null
        System.out.println("我是静态全局引用变量:" + chaos); //null
        System.out.println("我是普通全局引用变量:" + chao); //null
        System.out.println("我是普通全局引用数组变量:" + nums); //null
        // System.out.println(nums[0]);//对于没有引用的对象,访问其中的元素或者方法会报NullPointerException
        System.out.println("我是普通全局引用数组变量(已初始化):" + ints); //[I@330bedb4 对象
        System.out.println("我是普通全局引用数组变量(已初始化)中的元素:" + ints[0]); // 默认值
        System.out.println("我是常量:" + CONSTANT_VALUE);

    }


}


/*
"C:\Program Files\Java\jdk1.8.0_333\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2022.2\lib\idea_rt.jar=59705:C:\Program Files\JetBrains\IntelliJ IDEA 2022.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_333\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_333\jre\lib\rt.jar;C:\Users\chaos\Desktop\work-master\findwordcode\Steam\target\classes;C:\Users\chaos\Desktop\work-master\findwordcode\newcode\target\classes;C:\Users\chaos\.m2\repository\org\testng\testng\7.6.1\testng-7.6.1.jar;C:\Users\chaos\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\chaos\.m2\repository\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;C:\Users\chaos\.m2\repository\com\beust\jcommander\1.82\jcommander-1.82.jar;C:\Users\chaos\.m2\repository\org\webjars\jquery\3.6.0\jquery-3.6.0.jar;C:\Users\chaos\Desktop\work-master\findwordcode\POJO\target\classes" com.chaossnow.InitializedTest
我是普通全局基本变量:0
我是静态全局基本变量: 0
我是静态全局引用变量:null
我是普通全局引用变量:null
我是普通全局引用数组变量:null
我是普通全局引用数组变量(已初始化):[I@330bedb4
我是普通全局引用数组变量(已初始化)中的元素:0
我是常量:3
*/

初始化(Initiatizion

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对**类变量(静态变量)**进行初始化。
两种方式:
声明类变量时指定初始值
使用静态代码块为其指定初始值

package com.chaossnow.streamtest;

/**
 * @program: findwordcode
 * @description:
 * @author: chaos
 * @create: 2022-09-11 16:10
 **/
public class InitializedDome {
    //静态全局变量
    public static int value = 3;
    public static String name;

    //静态代码块
    static {
        name = "I am Initialize";
    }

    //静态代码块
    static {
        System.out.println("我是静态代码块中的Name:" + name);
        System.out.println("我是静态代码块中的value:" + value);
    }
    
    //静态代码块
    static {
        value = 6;
    }

    public static void main(String[] args) {

        System.out.println("我是静态代码块中的main方法中name: " +name);
        System.out.println("我是静态代码块中的main方法中value: " + value);
    }
}

JVM初始化顺序:
如果this.class 还没有加载(ClassLoader)或连接(Linking),则程序先加载并连接this.class
如果this.class 的直接父类还没有被初始化,则先初始化其父类
如果类中有初始化语句,系统依次执行这些初始化语句。
初始化顺序:

public class Father{
    public static String staticField = "父亲-静态变量";
    public String  field = "父亲-普通变量";

    //初始化块
    {
        System.out.println(field);
        System.out.println("父亲-初始化代码块");
    }
    //静态初始化块
    static {
        System.out.println(staticField);
        System.out.println("父亲-静态代码块");
    }

    public void work(){
        System.out.println("父亲-工作");
    }


    public Father(){
        System.out.println("父亲-构造方法");
    }


}

package com.chaossnow.initializzed;

/**
 * @program: findwordcode
 * @description:
 * @author: chaos
 * @create: 2022-09-11 16:25
 **/
public class Son extends Father{

    public static String staticField = "儿子-静态变量";
    public String  field = "儿子-普通变量";

    //初始化块
    {
        System.out.println(field);
        System.out.println("儿子-初始化代码块");
    }
    //静态初始化块
    static {
        System.out.println(staticField);
        System.out.println("儿子-静态代码块");
    }

    public void work(){
        System.out.println("儿子-工作");
    }


    public Son(){
        System.out.println("儿子-构造方法");
    }

    public static void main(String[] args) {
        System.out.println("main 方法");
        new Son();
    }

}




/*
父亲-静态变量
父亲-静态初始化代码块
儿子-静态变量
儿子-静态初始化代码块
main 方法
父亲-普通变量
父亲-初始化代码块
父亲-构造方法
儿子-普通变量
儿子-初始化代码块
儿子-构造方法
*/

/*
不考虑继承情况
静态变量、静态代码块
变量,非静态代码块
构造函数
    */

原则:

  • 首先执行static初始化,先执行父类的static,在执行子类的static。这是因为子类的成功初始化可能与父类的初始化有关。
  • 再初始化普通变量和普通代码块
  • 最后执行构造方法。

https://blog.csdn.net/qq_25665807/article/details/74452181

类初始化的时机:只有在对类的主动使用,才会导致类的初始化
  • 创建类的实例,new
  • 访问类的静态变量或者调用类的静态方法
  • 反射
  • 初始化子类,父类也会被初始化
  • Java虚拟机启动时表明为启动类的类(Java Test)

卸载

  • 使用System.exit() 方法
  • 正常执行结束
  • 执行过程中遇到了错误或者异常导致的异常终止
  • 操作系统错误导致java虚拟机终止

JVM类加载机制

**全盘负责:**当一个ClassLoader负责加载某个Class的时候,该Class所依赖或引用的Class也由该ClassLoader负责载入,除非显式声明。
**父类委托:**先让父类加载器进行加载,只有在父类加载器无法加载该类的时候才会启用本类进行加载。
**缓存机制:**缓存机制会保证所有加载过的Class都写入缓存。优先加载ClassLoader缓存区里面的数据。找不到才会重新读取该类的二进制数据。
**双亲委派机制:**如果一个类加载器收到了类加载请求,自己不会首先就去尝试加载这个类。而是把请委托给自己的父加载器去完成,依次向上。所有的类加载请求最终都应该被传递到最顶层的类加载器中,只有父类在他的搜索范围没有找到所需要的类的时候,子ClassLoader才会自己尝试去加载。

双亲委派机制:

  • AppClassLoader 加载一个class时,它自己不会直接去加载,把类加载请求委派给父类加载器ExtensionClassLoader来加载。
  • ExtensionClassLoader加载一个class,他自己也不会直接去加载,他会把类加载请求委派给父类加载器BootstrapClassLoader去加载。
  • 当BootstrapClassLoader加载失败(%JAVA_HOME%/lib中没有找到该class),会使用ExtensionClassLoader来尝试进行加载。
  • 如果ExtClassLoader也加载失败,则会使用AppClassLoader来进行加载,如果AppClassLoader也加载失败,则会爆出ClassNofFountException
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值