jvm的类加载器_类加载器有几种

jvm的类加载器_类加载器有几种一、概述虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、概述

虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为类加载器。

对于类加载,我们关注两个方面的问题:

  • JVM定义的三个类加载器(Bootstrap,Extension,System)
  • 双亲委派模型(机制,JDBC违背案例)

二、类加载器

JVM使用以下三种类型的类加载器:

  • 启动类(Bootstrap)类加载器

    这个类存放在<JAVA_HOME>\lib目录中,无法被Java程序直接引用;

  • 扩展(Extension)类加载器

    这个类加载器负责加载<JAVA_HOME>\lib\ext目录中的所有类库,开发者可以直接使用;

  • 引用程序(Application)类加载器

    这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器,负责加载用户类路径上所指定的类库。开发者可以直接使用。

从java虚拟机的角度来说,一种是启动类加载器,是JVM的一部分;另一种是其他类加载器,独立于JVM,全部继承自抽象类java.lang.ClassLoader。只有其他类加载器程序员才能自己使用。

三、双亲委派模型

1.什么是双亲委派模型

JVM在加载类时默认采用的是双亲委派机制。

通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,然后父类加载器再委托给它的父类……以此类推,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。

只有父类加载器无法完成加载请求时,才会使用子类加载器去加载

双亲委派模型的一个重要作用是为了保证类加载过程的安全性

  • 假设有一个开发者自己编写了一个名为 java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义 ClassLoader来加载自己编写的 java.lang.Object类。
  • 但是无论哪一个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,启动类加载器在其搜索范围内可以搜索到的只有rt.jar中的java.lang.Object
  • 这样就确保了Object类的唯一性,如果是String也是同理。

也因此,java判断类是否是同一个类,就是通过判断类的ClassLoader.class来判断的,举个简单的例子:只要不是一个爹妈生出来的,哪怕长的一模一样那也不是亲兄弟。

2.类的加载顺序

我们打开ClassLoader类,找到loadClass()方法,可以在注释上看到如何按顺序搜索类的:

*   Invoke {@link #findLoadedClass(String)} to check if the class
*   has already been loaded.  </p></li>
*
*   Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
*   on the parent class loader.  If the parent is <tt>null</tt> the class
*   loader built-in to the virtual machine is used, instead.  </p></li>
*
*   Invoke the {@link #findClass(String)} method to find the
*   class.

即:

  • 使用findLoadedClass()方法检查类是否已经加载;
  • 调用父类加载器的loaderCalss()方法。如果父类是null,就直接调用虚拟机内置的Bootstrap加载;
  • 如果父类无法加载,使用自己findClass()加载。

我们再看一下loadClass()方法的源码,更直观的展示了双亲委派模型下是如何加载类的:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException {
    //线程安全,保证只有一个线程能对类初始化
    synchronized (getClassLoadingLock(name)) {
        //1.检查类是否已经被加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //2.如果没有被加载,判断是否存在父类加载器
                if (parent != null) {
                    //3.有就递归委派给父类加载器加载
                    c = parent.loadClass(name, false);
                } else {
                    //4.否则就使用启动类加载器
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 如果到了启动类加载器还找不到类,则抛出ClassNotFoundException
            }
		
            //如果父类加载器无法加载,就使用自身的findClass加载
            if (c == null) {
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

这里就是为什么JDK在我们自定义类加载器的时候不推荐重写loadClass()只让我们重写findClass()方法的原因了,因为如果重写了方法,却不遵守双亲委派模型,就有可能导致不可预料的后果。

当然,不推荐归不推荐,如果有必要的话仍然可以重写loadClass()方法,比如JDBC、JNDI就打破了双亲委派模型

3.三种类加载器的关系

三种类加载器的关系

我们运行一下以下代码:

public static void main( String[] args ) {
    System.out.println("应用程序加载器:"+ClassLoader.getSystemClassLoader());
    System.out.println("应用程序加载器的父类:"+ClassLoader.getSystemClassLoader().getParent());
    System.out.println("扩展加载器的父类:"+ClassLoader.getSystemClassLoader().getParent().getParent());
}

//输出结果
应用程序加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
应用程序加载器的父类:sun.misc.Launcher$ExtClassLoader@29453f44
扩展加载器的父类:null

由于启动类加载器是jvm内置的,我们无法直接调用,所以返回值是null。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170794.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • int转换为char数组_C语言将整数转化为字符串

    int转换为char数组_C语言将整数转化为字符串如inti=1;在程序中直接将强制将i转换成char类型chara=(char)i,会发现a并不是’1’而是’\0001′,原因是在将i转换成char时,默认的会把i的值当成ASCII值,这样a的值就是’\0001’了

    2022年10月19日
  • 软件设计规格说明书案例(软件需求规格说明书范文)

    软件设计规格说明书转载于:https://www.cnblogs.com/miemieda123/p/9117368.html

  • 海龟作图—用Python 绘图[通俗易懂]

    原文地址:http://www.epubit.com.cn/book/onlinechapter/37788在本章中,我们将编写简短的、简单的程序来创建漂亮的、复杂的视觉效果。为了做到这一点,我们可以使用海龟作图软件。在海龟作图中,我们可以编写指令让一个虚拟的(想象中的)海龟在屏幕上来回移动。这个海龟带着一只钢笔,我们可以让海龟无论移动到哪都使用这只钢笔来绘制线条。通过编写代码,以各种很酷

  • 卷积神经网络常用模型_keras 卷积 循环 多分类

    卷积神经网络常用模型_keras 卷积 循环 多分类刚刚接触到深度学习,前2个月的时间里,我用一维的卷积神经网络实现了对于一维数据集的分类和回归。由于在做这次课题之前,我对深度学习基本上没有过接触,所以期间走了很多弯路。在刚刚收到题目的要求时,我选择使用TensorFlow来直接编写函数,结果由于没有什么基础,写了一个周我就放弃了,改用keras来完成我的任务。用keras来搭建神经网络其实很简单。网络的前面部分都是在提取一维信号的特征…

  • ubuntu12.04内核版本_ubuntu内核版本查看

    ubuntu12.04内核版本_ubuntu内核版本查看下载内核:http://kernel.ubuntu.com/~kernel-ppa/mainline/下载对应的deb文件。根据自己需求选择版本。我安装的是4.10版本:对于64位系统:wgethttp://kernel.ubuntu.com/~kernel-ppa/mainline/v4.10.1/linux-headers-4.10.1-041001_4.10.1-041001.2…

  • python求和怎么编程_python字符串数字求和

    python求和怎么编程_python字符串数字求和广告关闭腾讯云11.11云上盛惠,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!的和实现环境:python3编辑器:pycharm分析:1、阶乘的计算就是比较麻烦的一部分,用递归函数实现是比较好的方案,先定义一个递归函数实现求阶乘功能。defrecursion(n):定义递归函数实现求阶乘功能ifn==1:return1else:returnn*recurs…

    2022年10月23日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号