jvm的类加载器(classloader)及类的加载过程

上文:类的加载时机


什么叫类加载(classloader)?

类加载简单的说就是JVM通过类加载器ClassLoader,把.class文件中的信息,拼装成Class对象放入内存中。

.java---complier----.class---classloader--Class

类加载过程是什么?

类的加载主要有三步:加载->连接->初始化。连接过程又分为 验证->准备->解析

加载(Load)

指的是类加载,即class loading,虚拟机加载完成三件事情:

    通过一个类的全限定名来获取定义此类的二进制字节流;

    将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;

    在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

注意:类加载即可以由引导类加载器或自定义类加载器去完成加载。(可用开发人员控制)

连接(Linking)

    连接分为,三大步骤分别为:验证、准备、解析

验证(Verification)

    为了确保Class文件的字节流中包含的信息符合虚拟机要求,并且不会危害虚拟机,所以验证分为四大验证阶段,分别为:文件格式验证、元数据验证、字节码验证、符号引用验证。

注意:

一个类方法的字节码没有通过字节码验证,那肯定是有问题;

如果一个方法体通过字节码验证,也不能表示一定就是安全的;

因为程序去校验程序逻辑是无法做到绝对准确。

准备(Preparation)

    目的:正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。

以下参考:深入理解 java 虚拟机第三版

解析(Resolution)

    解析目的:Java虚拟机将常量池内的符号引用替换为直接引用的过程,相关解析有:类或接口的解析、字段解析、方法解析、接口方法解析。

什么是符号引用?

    以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。

什么是直接引用(Direct References)?

    可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

区别于符合引用就是直接引用必须引用的目标已经在内存中存在。

    初始化(Initialization)

    初始化是类加载过程中最后一步,初始化目的:根据程序员程序编码制定的主观计划去初始化类变量和其他资源。

JAVA ClassLoader(类加载器)分类

    Bootstrap Classloader(启动类加载器)

最顶层的加载类,由C或C++语言实现。主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。

Extension ClassLoader(扩展类装载器)

主要负责加载Java的扩展类库,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。

system classloader(系统类装载器)

    也称为Appclass Loader 加载当前应用的classpath的所有类。

加载顺序?

Bootstrap Classloader->Extension ClassLoader->system classloader

代码实现

/**
 * @author: csh
 * @Date: 2020/12/28 18:35
 * @Description:classLoader学习
 */
public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取系统类加载器 sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取拓展类加载器 sun.misc.Launcher$ExtClassLoader@14ae5a5
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);

        //获取根类加载器 null
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);

        //用户自定类加载器 sun.misc.Launcher$AppClassLoader@18b4aac2(使用系统加载器进行加载)
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
        //获取strng 类加载器 null(使用引导类加载器)java核心都是使用该种加载方式
        ClassLoader stringClassLoader = String.class.getClassLoader();
        System.out.println(stringClassLoader);
    }
}

结果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@14ae5a5
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

个人理解:引导类加载器和自定义类加载器,自定义类就像你自己的手机随时想用就用,或者你家人想用直接向你借就OK了,但是引导类就像某个大领导的个人手机,你跟你家人一般是无法直接借到的(基本不可能)。

全盘负责委托机制是什么?

全盘负责委托机制指的是:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器 (而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。

双亲委托是什么?

即在类加载的时候,系统会判断当前类是否已经被加载,如果已经被加载,就会直接返回可用的类,否则就会尝试加载,在尝试加载时,会先请求双亲处理,如果双亲请求失败,则会自己加载。

注意:双亲为null有两种情况:第一,其双亲就是启动类加载器;第二,当前加载器就是启动类加载器。判断类是否加载时,应用类加载器会顺着双亲路径往上判断,直到启动类加载器。但是启动类加载器不会往下询问,这个委托是单向的。

protected synchronized Class<?> loadClass ( String name , boolean resolve ) throws ClassNotFoundException{
        //检查指定类是否被当前类加载器加载过
        Class c = findLoadedClass(name);
        if( c == null ){//如果没被加载过,委派给父加载器加载
            try{
                if( parent != null )
                    c = parent.loadClass(name,resolve);
                else 
                    c = findBootstrapClassOrNull(name);
            }catch ( ClassNotFoundException e ){
                //如果父加载器无法加载
            }
            if( c == null ){//父类不能加载,由当前的类加载器加载
                c = findClass(name);
            }
        }
        if( resolve ){//如果要求立即链接,那么加载完类直接链接
            resolveClass();
        }
        //将加载过这个类对象直接返回
        return c;
    }

双亲委托模型好处与弊端

好处:

避免重复加载:当父亲已经加载了该类,就没有必要ClassLoader再加载一次;

安全:避免自定义的类名与原生一致导致替代java核 心的api类型;

弊端:

顶层的classloader无法访问底层的classloader所加载的类。

最后

虚拟机加载过程及类的加载器相关知识的了解,有利于今后自定义一些jar包的时候可以做为基础知识进行深入。比如当了解自定义类加载器后可以在些基础上开发对应的动态jar包进行相关的动态加载。

参考文章:

https://blog.csdn.net/n20164206199/article/details/106769415

https://www.iteye.com/blog/chenzhou123520-1601267

系列文章:

HotSpot虚拟机对象如何被创建的?

jvm结构说明

jvm相关知识

java发展史及虚拟机历史

类的加载时机


本人工作之余在线指导新同学,解答相关技术问题及行业情况,可以加下面QQ,记得备注问题,不备注不加,谢谢!


更多可关注:微信公众号 技术趋势


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM类加载是由类加载器及其子类实现的。类加载器Java运行时系统的重要组成部分,负责在运行时查找和加载类文件中的类。在JVM中,类加载器按照一定的层次结构进行组织,每个类加载器负责加载特定位置的类。其中,启动类加载器(Bootstrap ClassLoader)是负责加载存放在<JAVA_HOME>/lib目录中的核心类库,如rt.jar、resources.jar等,同时也可以加载通过-Xbootclasspath参数指定的路径中的类库。启动类加载器是用C语言编写的,随着JVM启动而加载。当JVM需要使用某个类时,它会通过类加载器查找并加载这个类。加载过程会经历连接阶段,包括验证、准备和解析。在验证阶段,JVM会确保加载的类信息符合JVM规范。在准备阶段,JVM会为类变量分配内存并设置初始值,在方法区中分配这些内存。在解析阶段,JVM会根据符号引用替换为直接引用,以便后续的使用。通过类加载器的协同工作,JVM能够在运行时动态加载类,从而实现Java的灵活性和跨平台性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [JVM类加载原理](https://blog.csdn.net/ChineseSoftware/article/details/119212339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [JVM类加载器](https://blog.csdn.net/rockvine/article/details/124825354)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值