精华内容
下载资源
问答
  • JVM双亲委派模型

    2021-05-21 18:03:34
    文章目录 双亲委派原理 类加载器三个机制 双亲委派的意义 JVM提供的类加载器 执行类加载的五种方式 自定义类加载器 (1)遵守双亲委派模型:继承ClassLoader,重写findClass方法。 (2)破坏双亲委派模型:继承...

    双亲委派原理

    除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的上层加载器,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给上层的加载器,如果上层类加载器还存在其上层类加载器,则进一步向上委托,依次递归,直到请求最终到达顶层的启动类加载器,从顶层类加载器开始,如果类加载器根据类的全限定名查询到已经加载过这个类,就成功返回加载过的此类信息,倘若加载器未加载过此类,则原路返回给下层加载器继续重复此过程,直到最先加载此类的加载器所有上层加载器都未加载过此类后,此类加载器才会尝试自己去加载,这便是双亲委派模式。

    在这里插入图片描述

    类加载器三个机制

    • 委托:指加载一个类的请求交给父类加载器,若父类加载器不可以找到或者加载到,再加载这个类
    • 单一性:指子类加载器不会再次加载父类加载器已经加载过的类
    • 可见性:子类加载器可以看见父类加载器加载的所有类,而父类加载器不可以看到子类加载器加载的类

    举个栗子:

    假如你是某个企业员工,你写了一份方案希望得到执行,首先你得拿给你的经理去审批吧,经理说这个事情他做不了主,于是经理就拿给总经理看,总经理也做不了主,就给董事长看,然后董事长看了看,也看不明白于是让总经理自己拿主意吧,总经理仔细一看,这个方案之前公司已经做过了,于是让经理去告诉那个员工不用做了,直接用那个做过的方案吧。

    双亲委派的意义

    采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当上层类加载器已经加载了该类时,就没有必要下层的ClassLoader再加载一次。

    其次是考虑到安全因素,jdk中定义的类不会被随意替换,假设我们在classpath路径下自定义一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器通过索引发现同全限定名的类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,那么你所定义的类就不会被加载,这样便可以防止核心API库被随意篡改

    如:

    package java.lang;
    
    public class Integer {
    
        public void print(){
            System.out.println("this is Integer.");
        }
    
        public static void main(String[] args) {
            new Integer().print();
        }
    }
    

    执行main方法后输出如下:

    在这里插入图片描述

    JVM提供的类加载器

    • 启动类加载器(BootstrapClassLoader):由jvm负责管理,主要负责加载核心的类库(**rt.jar,也就是java.lang.***等),并构造和启动ExtClassLoader和APPClassLoader。

    • 扩展类加载器(ExtClassLoader):主要负责加载jre/lib/ext**目录下的一些扩展的jar。

    • 应用类加载器(AppClassLoader):主要负责加载classpath下jar包和应用程序的主函数类。

    如果当前类加载器加载的类引用了其它类,那么也会通过递归的方式先对其所有引用进行加载。

    执行类加载的五种方式

    认识了这三种类加载器,接下来我们看看类加载的五种方式。

    1. 通过命令行使用java命令启动应用时由JVM初始化加载含有main()方法的主类。
    2. 使用new关键字初始化一个对象时
    3. 通过类对应的Class对象的newInstance()方法
    4. 通过Class.forName(name)方法动态加载
    5. 通过ClassLoader.loadClass(name)方法动态加载

    自定义类加载器

    java系统为我们提供的三种类加载器,还给出了他们的层次关系图,最下面就是自定义类加载器,那么我们如何自己定义类加载器呢?这主要有两种方式

    (1)遵守双亲委派模型:继承ClassLoader,重写findClass方法。

    通常我们推荐采用此方式自定义类加载器,最大程度上的遵守双亲委派模型。

    findClass(name)查找具有指定全限定名的类。此方法用于遵循加载类的委托模型的类装入器实现重写,并将在检查请求类的父类装入器之后由loadClass方法调用。如果该类没有被加载过且其class文件读取不到则抛出ClassNotFoundException异常。

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
    

    其实现例子:

    package com.aliencat.javabase.classloader;
    
    public class MyClassLoader extends ClassLoader{
    
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            switch (name){
                case "java.lang.Integer":return Double.class;
                case "com.aliencat.javabase.classloader.LoaderDemo" :  return loadClassFromDisk(name);
            }
            throw new ClassNotFoundException(name);
        }
    
        //从calsspath下加载类的字节码文件
        public byte[] loadClassFromDisk(String name) {
            String classPathRoot = Thread.currentThread().getContextClassLoader().getResource("").getPath();
            classPathRoot = classPathRoot.substring(1);
            String filePath = classPathRoot + name.replace(".","/") + ".class";
            try(InputStream in = new FileInputStream(filePath) ;
                ByteOutputStream stream = new ByteOutputStream()) {
                byte[] buff = new byte[1024];
                for(int num = 0; (num=in.read(buff)) != -1;){
                    stream.write(buff,0,num);
                }
                return stream.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        public static void main(String[] args) throws ClassNotFoundException {
            Class clzz = new MyClassLoader().loadClass("com.aliencat.javabase.classloader.LoaderDemo");
            System.out.println(clzz);
            clzz = new MyClassLoader().loadClass("java.lang.Integer");
            System.out.println(clzz);
            clzz = new MyClassLoader().loadClass("java.lang.String");
            System.out.println(clzz);
            clzz = new MyClassLoader().loadClass("java.lang.xxxxx");
            System.out.println(clzz);
        }
    }
    

    输出如下:

    在这里插入图片描述

    (2)破坏双亲委派模型:继承ClassLoader,重写loadClass方法。

    我们先来看下loadClass方法的源码是怎样的:

        protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // 首先,在当前加载器加载过的类中检查这个类有没有被加载过
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            //存在上层类加载器,则让上层取执行loadClass方法
                            c = parent.loadClass(name, false);
                        } else {
                            //让BootstrapClass类加载器去查找该类
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // 上层类加载器抛出了ClassNotFoundException 则不进行处理
    
                    }
    
                    if (c == null) {
    
                        long t1 = System.nanoTime();
                        // 如果上层类加载器都没有找到
                        // 那么这个类加载器自己去找
                        // 如果找到了,则将resolve置为true
                        c = findClass(name);
    
                        // 这是定义类加载器;记录统计数据
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    //解析此类的信息
                    resolveClass(c);
                }
                return c;
            }
        }
    

    示例如下:

    public class MyClassLoader extends ClassLoader {
    
        public static void main(String[] args) throws ClassNotFoundException {
            Class clzz = new MyClassLoader().loadClass("com.aliencat.javabase.classloader.ClassTest");
            System.out.println(clzz);
            clzz = new MyClassLoader().loadClass("java.lang.Double");
            System.out.println(clzz);
            clzz = new MyClassLoader().loadClass("java.lang.String");
            System.out.println(clzz);
        }
    
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            switch (name) {
                case "java.lang.Double":
                    return Integer.class;
                case "java.lang.Object":  //如果去掉此项,则破坏双亲委任的情况下会报找不到Object的NoClassDefFoundError异常
                    return Object.class;
                case "com.aliencat.javabase.classloader.ClassTest":
                    byte[] bytes = loadClassFromDisk(name);
                    if(bytes != null){
                        return defineClass(name,bytes,0,bytes.length);
                    }else {
                        return null;
                    }
            }
            throw new ClassNotFoundException(name);
        }
    
        //从calsspath下加载类的字节码文件
        public byte[] loadClassFromDisk(String name) {
            String classPathRoot = Thread.currentThread().getContextClassLoader().getResource("").getPath();
            classPathRoot = classPathRoot.substring(1);
            String filePath = classPathRoot + name.replace(".","/") + ".class";
            try(InputStream in = new FileInputStream(filePath) ;
                ByteOutputStream stream = new ByteOutputStream()) {
                byte[] buff = new byte[1024];
                for(int num = 0; (num=in.read(buff)) != -1;){
                    stream.write(buff,0,num);
                }
                return stream.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            synchronized (LoaderDemo.class) {
                // 首先,在当前加载器加载过的类中检查这个类有没有被加载过
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    //没加载过的话就去磁盘对应路径下去找
                    c = findClass(name);
                }
                return c;
            }
        }
    
    }
    
    class ClassTest{
    
    }
    

    输出如下:

    在这里插入图片描述

    所以破坏双亲委托的方法简单来说就是通过继承ClassLoader重写loadClass方法,去掉其中委托给上级加载类的相关逻辑然后实现自定义的加载类的findClass逻辑。(另外你可以试试把ClassTest替换String的类名是什么效果哦,我就不演示了)

    Q:前面说了一堆双亲委托的好处,那么为什么要破坏双亲委托呢?

    A:因为在某些情况下父类加载器需要委托子类加载器去加载class文件。受到加载范围的限制,父类加载器无法加载到需要的文件,以JDBC接口为例,由于JDBC接口定义在jdk当中的,而其实现由各个数据库的服务商来提供,比如mysql的就写了MySQL-Connector,那么问题就来了,JDBC接口由启动类加载器加载,而JDBC的实现是由服务商提供的,并不在启动类加载器的加载目录下,在不破坏双亲委派的情况下,启动类加载器下层的类加载器要加载JDBC则必然会返回启动类加载器中已经加载过的接口,那么服务商提供的JDBC就不会被加载,所以需要自定义类加载器破坏双亲委派拿到服务商实现的JDBC接口,这里仅仅是举了破坏双亲委派的其中一个情况,类似的还有tomcat。

    展开全文
  • JVM 双亲委派模型

    2021-05-12 17:10:36
    只有当父类加载器无法完成加载请求(也就是搜索范围内无该类)子加载器才会尝试自己去加载这个类这也就是双亲委派机制 使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系防止...

    从虚拟机的角度来讲只有两种加载器一种是启动类加载器,另一种是其他类的加载器,但是他也是继承启动类加载器的

    从java来讲有三种加载器

    启动(Bootstrap)类加载器:启动类加载器是用本地代码实现的类加载器,可由启动类加载器加载到的路径可通过System.getProperty(“sun.boot.class.path”)查看。

    扩展(Extension)类加载器:扩展类加载器是ExtClassLoader实现的,它负责将JAVA_HOME /lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器,具体可由扩展类加载器加载到的路径可通过System.getProperty(“java.ext.dirs”)查看。

    系统(System)类加载器:AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,它负责将用户类路径(就是第三方库),可以直接使用系统类加载器,具体可由系统类加载器加载到的路径可通过System.getProperty(“java.class.path”)

    还有一种是自定义类加载器

    自定义类加载器-》系统类加载器-》扩展类加载器-》启动类加载器

    一个类的加载过程
    如果一个类加载器收到了类加载请求,它不会首先加载这个类,而是将请求委派给父类加载器去完成
    所有的加载请求最终都委派给顶层的引导类加载器,只有当父类加载器无法完成加载请求(也就是搜索范围内无该类)子加载器才会尝试自己去加载这个类这也就是双亲委派机制

    使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系防止程序混乱

    什么时候需要自定义类加载器?
    加密:防止代码泄露的时候,可以对编译后的文件加密,然后再通过自定义加载器解密加载

    从非标准的来源加载代码:从指定来源加载字节码文件

    动态创建:动态创建代码可以使用

    展开全文
  • jvm 双亲委派模型

    2021-08-03 09:50:36
    jvm中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器以外,其余的类加载器都应该有自己的父级类加载器。这里的父子关系是组合而不是继承,工作过程如下: 1.一个类的加载器收到类的加载请求以后,首先...

    Java13的双亲委派模型:

    jvm中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器以外,其余的类加载器都应该有自己的父级类加载器。这里的父子关系是组合而不是继承,工作过程如下:

    1.一个类的加载器收到类的加载请求以后,首先搜索他的内建加载器定义的所有“具名模块”

    2.如果找到了合适的模块定义,将会使用该加载器来加载

    3.如果class没有在这些加载器定义的具名模块中找到,那么将会委托给父类加载器,直到启动类加载器。

    4.如果父级加载器反馈它不能完成加载请求,比如在他的搜索路径下找不到这个类,那子的类加载器才来自己加载

    5.在类路径下找不到的类将成为这些加载器的无名模块

    Java8 的双亲委派模型:

    如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

     

    双亲委派模型的机制和作用:

    1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
    2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。


    破坏双亲委派模型:


    参考博客:

     

    https://blog.csdn.net/hu_zhiting/article/details/107320391

    展开全文
  • 【高级开发进阶】1.1.3 双亲委派模型及如何打破

    千次阅读 多人点赞 2021-02-03 20:24:19
    首先得知道什么是双亲委派模型?为什么要打破它?打破它用途是什么? 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。 上面所介绍的这几种类加载器的层次关系,称为类加载...

    首先得知道什么是双亲委派模型?为什么要打破它?打破它用途是什么?

    双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器

    上面所介绍的这几种类加载器的层次关系,称为类加载器的双亲委派模型

    类随着它的类加载器一起具备了一种带有优先级的层次关系

    例如类java.lang.Object,它由启动类加载器加载。双亲委派模型保证任何类加载器收到的对java.lang.Object的加载请求,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并用自定义的类加载器加载,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。

    那为什么要打破它?

    展开全文
  • 深入JVM 双亲委派模型jvm】通过JDBC为例谈谈双亲委派模型的破坏 为什么JVM的类加载要采用双亲委派的加载机制? Java对象创建方式 父类代码和子类代码如下: //父类 public class SuperClass { private String ...
  • JVM-双亲委派模型

    2021-09-14 16:44:45
    JVM中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器外,其余的类加载器都应该有自己的父级加载器。这里的父子关系是组合而不是继承(如自定义加载器的父级是app加载器,app加载器的父级是平台加载器,...
  • 双亲委派模型 JVM提供了3种类加载器: •启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。 •...
  • jvm 的主要组成部分类加载器(ClassLoader)运行时数据区(Runtime Data Area)执行引擎(Execution Engine)本地库接口(Native Interface)jvm 运行时数据区的组成方法区:①方法区主要用来存储已被虚拟机加载的类信息...
  • 一、双亲委派机制 Java虚拟机堆class文件采用的是按需加载的方式,也就是说只有在使用的时候才会将对应的class文件加载到内存中,生成class对象。 在加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把...
  • JVM 双亲委派机制

    2021-05-21 12:24:19
    文章目录类加载器双亲委派机制1.工作原理:2.源码3.双亲委派机制的好处和缺陷 类加载器 实现通过类的权限名获取该类的二进制流字节代码叫做类加载器(类加载器本身也是个类) 类加载器可以分为:启动类加载器,扩展类...
  • JVM双亲委派模型 本文有十下亲自书写,资料源自网络,内容由自己的总结和理解产生,禁止搬运! 一、先谈类加载器 什么是类加载器 Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二...
  • 今天我们就来盘一盘这个面试题,不过在说双亲委派模型之前,我们得先简单了解下类加载。 类加载 我们平常写的代码是保存在一个 .java文件里面,经过编译会生成.class文件,这个文件存储的就是字节码,如果要用上我们...
  • 类的加载过程是由类加载器来完成,类加载器由JVM提供。我们开发人员也可以通过继承ClassLoader来实现自己的类加载器。 加载的class来源 从本地文件系统内加载class文件 从JAR包加载class文件 通过网络加载class文件...
  • 此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。 而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为...
  • 1、什么是双亲委派模型 已知每个类都有对应的类加载器,系统的ClassLoader在协同工作时会默认使用双亲委派模型:在类加载时先判断该类是否被加载过,已加载过的类会直接返回。没加载过的类加载的时候,先把该请求...
  • 双亲委派模型

    2021-03-28 15:52:12
    双亲委派模型 概念:加载类的时候,⾸先会把该请求委派该⽗类加载器的 loadClass() 处理,因此所有的 请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当⽗类加载器⽆法处理 时,才由⾃⼰来处理。...
  • JVM - 双亲委派机制的优势和劣势

    千次阅读 2021-03-16 19:32:08
    双亲委派机制的优势和劣势 双亲委派机制优势 避免类的重复加载, 确保一个类的全局唯一性 Java 类随着它的类加载器一起具备了一种带有优先级的层级关系, 通过这种 层级关系可以避免类的重复加载, 当父亲已经加载了...
  • 上篇文章我们主要讲了类加载过程,双亲委派模型,和JVM内存模型,本篇文章我们主要讲垃圾回收算法,垃圾回收器,通过上篇文章,我们知道JVM内存主要包括五大块:1. 堆内存,2. 方法区(元空间),3. 本地方法栈,4. ...
  • JVM 破坏双亲委派模型

    2021-12-05 23:27:58
    双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式。在 Java的世界中大部分的类加载器都遵循这个模型,但也有例外的情况,直到Java模块化出现为止,双亲委派模型主要...
  • SPI如何破坏双亲委派模型 # 双亲委派# SPI 类加载器(classloader) 先从类加载器说起,凡事先问是什么,首先什么是类加载器? 我们知道,一个 *.java 的代码源文件要执行起来之前,必须通过 javac 构建抽象...
  • JVM 双亲委派机制(通俗易懂)

    千次阅读 2021-01-20 11:30:25
    Java是运行在Java的虚拟机(JVM)中的,我们在IDE中编写的Java源代码被编译器编译成.class的字节码文件。然后ClassLoader负责将这些class文件加载到JVM中去执行。JVM中提供了自上而下提供了三层的ClassLoader: ...
  • Java是运行在Java的虚拟机(JVM)中的,在初步学习Java时,我们都知道,编写的Java源代码会被编译器编译成.class的字节码文件。然后ClassLoader负责将这些class文件给加载到JVM中去执行。 我们跑Java程序,跟JVM脱不了...
  • 双亲委派模型的好处: ·主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String。·同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader...
  • 在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。 类加载器就是根据指定全限定名称将 class ...
  • 双亲委派模型就是为了控制这种) 几种类加载器 · 启动类加载器 Bootstrap ClassLoader 加载JAVA_HOME 下的lib 下面的类库,是JVM的一部分,使用C++ 实现。启动类加载器无法被java程序直接使用。 · 扩展类...
  • 双亲委派模型JVM 类加载讲个故事:以前,爱捣鼓的小明突然灵机一动,写出了下面的代码package java.lang;/*** @author dengchengchao* @date 2019/2/26 19:20*/public class String {//...复制真正String的其他...
  • >元数据区(JDK1.8、线程共享)2.JVM类加载过程(Class Loading)3.JVM双亲委派模型(JDK1.2)4.JVM垃圾回收器(1)判断死亡对象①引用计数器算法②可达性分析算法(目前JVM使用的判断对象死亡的算法)(2...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,781
精华内容 9,512
关键字:

jvm双亲委派模型