栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JVM——类加载器(Bootstrap、Extention、Application、自定义类加载器,步骤,双亲委派机制)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JVM——类加载器(Bootstrap、Extention、Application、自定义类加载器,步骤,双亲委派机制)

一、概述 1、作用

类加载器是负责搬运.class到JVM中去的,这些.class文件开头都有特定的标识,把.class文件中的字节码内容加载到内存中去,并把这些内容转换成方法区中的运行时数据结构,注意它只负责加载.class文件

2、parent属性
类加载器描述
Bootstrapparent是null,因为是c++实现的,java没法直接引用
Applicationparent的Extension ClassLoader
Extensionparent是null
二、步骤

从类加载到释放内存,总共经过了7个步骤:加载,连接(验证、准备、解析),初始化,使用,卸载。

1、加载

  ①将.class文件载入内存;
  ②将静态数据结构转化成方法区中的运行时数据结构
  ③在堆上生成一个代表这个类的java.lang.Class对象作为数据访问入口

2、连接

(1)验证
  确保加载的类符合JVM规范、安全,保证被校验类的方法在运行时不会做危害虚拟机的事,就一个安全检查;
(2)准备
  为static变量在方法区中分配内存空间,设置变量初始值,如:“static int count = 3;”。
(3)解析
  JVM将常量池中的符号引用替换为直接引用。比如“import java.util.List;”,这就是符号引用,直接引用就是指针或者对象地址,引用对象是在内存中进行的。

3、初始化

其实就是赋值,它会执行一个类构造器的()方法,由编译器自动收集类中的所有变量的赋值动作,就是前面的3赋值给count。

4、卸载

  GC把无用对象从内存中清除。

三、类加载器的加载顺序

加载Class类是有优先级的。

其中BootStrap ClassLoader在虚拟机中,其他都在外部,用java实现,继承抽象类 java.lang.ClassLoader。

非BootStrap的类加载器,全部继承java.lang.classLoader,Java提供了两种实现:Extension ClassLoader、Application ClassLoader。

1、启动类加载器(BootStrap ClassLoader)

如rt.jar。该加载器无法直接被Java程序引用,用户自定义类加载器时,使用null代替可以把加载请求委派给该类加载器。

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

加载扩展的jar包,ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的,开发者可以直接使用扩展类加载器。

3、应用程序类加载器(Application ClassLoader)

由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

4、自定义类加载器(Customer ClassLoader)
类加载器加载路径虚拟机内外部
启动类加载器(Bootstrap ClassLoader)lib或者-Xbootclasspath 参数指定的路径,且文件名是JVM识别的内部
扩展类加载器(Extention ClassLoader)/lib/ext 或者被 java.ext.dir 系统变量指定路径的所有类库外部
应用程序类加载器(Application ClassLoader)用户类路径(ClassPath)上所指定的类库外部
自定义类加载器(Customer ClassLoader)外部

我们的应用程序都是由上述这三种类加载器互相配合从而实现类加载,如果有必要,还可以加入自己定义的类加载器。

四、双亲委派机制

委派什么?谁委派谁?为什么要委派?

类收到加载请求时,不会先自己尝试加载,而是委托给父类,只有父类加载器无法完成(找不到加载所需class)时,子类才会自己加载。可知,所有加载请求都会达到最顶层的Bootstrap ClassLoader。

1、父类加载器先尝试加载的好处

听上去有些奇怪,但是有很明显的优势。

(1)优先级的层次关系,从而使得基础类得到统一

比如java.lang.Object存放在rt.jar中,如果又写了一个存到class path中,那么,程序中用到的Object是哪个呢,答案是rt.jar中的那个,前面提到,rt.jar在Bootstrap ClassLoader中,而ClassPath在Application ClassLoader中,Bootstrap优先级更高。

即使是同一个.class,用不同类加载器加载,会得到两个类,因为每个类加载器都有独立的类名称空间。

(2)内存宝贵

没必要弄两份浪费,同一个Class对象,多个地方用,没必要加载多份,用同一份即可,但如果没有委派机制,就没办法去找父加载器中获取,可能父级有,这样就有多份了。

其实现主要在java.lang.ClassLoader 的 loadClass() :

2、java.lang.ClassLoader loadClass()源码
    protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    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;
        }
    }

简单讲下这个方法,当被调用的时候,先看下自己有没有加载过这个类,加载过了就直接返回,没加载过就先调用父类的loadClass(),让父类去加载。

如果父类发现自己没加载过,就去找爷爷,一直到parent是null,说明已经走到BootStrap ClassLoader了,就调用findBootstrapClassOrNull()(native方法)让它试试。

如果类是classpath下的,那么Bootstrap和Extension都会加载失败,最后回到自己这里,然后才会调用findClass(),自己去加载。

3、破坏双亲委派机制

场景常见于:SPI、OSGi(java模块化规范,热拔插、热部署、模块化,会有模块间类加载的问题)、Java Web服务器(Tomcat)。

常见SPI(Service Provider Interfaces),java为服务器提供的接口,JDBC、JNDI、JAXP等,Spring Boot的Ioc容器加载页用到了类似机制。

(1)jdbc

第三方服务商提供的jar,都是放在classpath下的,所以Bootstrap ClassLoader也爱莫能助。

所以在java.lang.Thread类里定义了一个线程上下文类加载器(Thread Context ClassLoader,TCCL),JVM主线程里这个就是默认的Application ClassLoader。去琢磨源码就会发现,居然出现了可以让父级类加载器,去使用子级类加载器(Application ClassLoader)。

(2)Tomcat

Web服务器需要运行多个web应用程序,这些应用依赖相同类库不同版本,需要把各个应用依赖的类库隔离。
好了,不细说了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/682625.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号