【JVM】教会你双亲委派模型

目录

在了解双亲委派模型之前,我们必须要知道:什么是类加载器?

类加载器

类加载器的定义:

在Java虚拟机的角度看,存在两种不同的类加载器: 

站在Java开发人员的角度来看,多种不同的类加载器:

启动类加载器(Bootstrap ClassLoader):

扩展类加载器(Extension ClassLoader):

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

类与类加载器

双亲委派模型

定义:

双亲委派模型代码示例:

双亲委派模型的工作流程:

双亲委派模型的作用:


各种类加载器之间的层次关系被称为类加载器的“双亲委派模型”

在了解双亲委派模型之前,我们必须要知道:什么是类加载器?

类加载器

类加载器的定义:

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

在Java虚拟机的角度看,存在两种不同的类加载器:

一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;

另一种是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

站在Java开发人员的角度来看,多种不同的类加载器:

启动类加载器(Bootstrap ClassLoader):

这是最顶层的类加载器,由 C/C++ 语言实现,是 JVM 自身的一部分。
它负责加载 JDK 中的核心类库,如 java.*、javax.* 等。
启动类加载器是无法被Java程序直接访问的。

扩展类加载器(Extension ClassLoader):

扩展类加载器是由 Java 语言实现的类加载器。
开发者可以直接使用扩展类加载器来加载自定义的类库
扩展类加载器的父加载器是启动类加载器。

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

应用程序类加载器也是由 Java 语言实现的类加载器。
它负责加载用户类路径上的所有类库。
开发者可以直接使用应用程序类加载器来加载自己编写的类
应用程序类加载器的父加载器是扩展类加载器。

类与类加载器

两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

这里,在不同的类加载器的情况下,用instanceof关键字看一下结果,来了解上边这句话的含义

写一个简单的类加载器,它可以加载与自己在同一路径下的Class文件:

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1)+".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object obj = myLoader.loadClass("com.jilit.classLoad.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof com.jilit.classLoad.ClassLoaderTest);
    }

}

运行结果:

两行输出结果中,从第一行可以看到这个对象确实是类com.jilit.classLoad.ClassLoaderTest实例化出来的,但在第二行的输出中却发现这个对象与com.jilit.classLoad.ClassLoaderTest做所属类型检查的时候返回了false。这是因为Java虚拟机中同时存在了两个ClassLoaderTest类,一个是由虚拟机的应用程序类加载器所加载的另外一个是由自定义的类加载器加载的虽然它们都来自同一个Class文件,但在Java虚拟机中仍然是两个互相独立的类,做对象所属类型检查时的结果自然为false。

双亲委派模型

定义:

各种类加载器之间的层次关系被称为类加载器的“双亲委派模型”

双亲委派模型代码示例:

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, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父类加载器抛出ClassNotFoundException
            // 说明父类加载器无法完成加载请求
        }
        if (c == null) {
            // 在父类加载器无法加载时
            // 再调用本身的findClass方法来进行类加载
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

双亲委派模型的工作流程:

1、如果一个类加载器收到了类加载的请求,类加载器首先会检查该类是否已经被加载过。

2、如果已经被加载:直接返回该类的实例。

3、如果未被加载:它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,如果父类加载器能够成功加载该类,则返回该类的实例。

4、只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

双亲委派模型的作用:

1、防止类的重复加载:在双亲委派模型中,一个类只会被加载一次。这是因为如果一个类已经被父加载器加载过,后续子加载器再次请求加载该类时,都会直接返回父加载器已加载的那个类。

2、隔离应用程序:
双亲委派模型可以有效地隔离不同的Java应用程序(每个应用程序都有自己的应用程序类加载器,它们加载的类互不干扰)这样可以避免一个应用程序中的类影响到其他应用程序,提高了系统的安全性。

3、保护 Java 核心库:
双亲委派机制可以防止用户自定义的类覆盖Java核心库中的类,确保了Java核心API的安全性,防止恶意代码的植入。(因为高级别的启动类加载器和扩展类加载器会先尝试加载类,如果能够加载成功,就不会交给低级别的应用程序类加载器去加载。)

4、增加可扩展性:双亲委派模型很好地支持了Java类加载机制的可扩展性。开发者可以自定义类加载器,且各个类加载器之间互不干扰,形成了一个相对完整的类加载器体系。

  • 40
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我不认为JVM可以“打破”双亲委派模型JVM使用双亲委派模型是为了保证类的加载和安全性,这是JVM的基本设计原则之一。 在双亲委派模型中,当一个类被加载时,首先会委托其父类加载器尝试加载该类。如果父类加载器无法加载该类,则会将该请求传递给其父类加载器的父类加载器,直到最终传递到启动类加载器。如果启动类加载器也无法加载该类,则会抛出ClassNotFoundException异常。 这种模型的好处是可以防止类的重复加载,并且可以保证类的安全性,因为只有在受信任的类加载器中加载的类才能访问受信任的资源。如果JVM打破了这种模型,可能会导致类的重复加载和不安全的类访问,从而破坏JVM的基本设计原则。 虽然JVM不能打破双亲委派模型,但是它提供了一些机制来绕过该模型。例如,可以使用Java的反射机制或者自定义类加载器来加载类,但是这些机制都需要开发者自己负责类的加载和安全性,因此需要谨慎使用。 ### 回答2: JVM双亲委派模型是一种类加载机制,用于保证Java程序的安全性和稳定性。在双亲委派模型中,JVM首先会查找并加载自定义类加载器的类,如果找不到,才会向上一级的父类加载器请求加载。这种模型的好处是可以避免类重复加载和类冲突,确保程序的运行稳定性。 但有时候,我们可能需要打破双亲委派模型,以满足特定的需求。下面是一些打破双亲委派模型的方式: 1. 自定义类加载器:可以自己编写一个类加载器,并重写其中的加载类的逻辑。这样就可以绕过父类加载器,直接加载自定义的类。 2. 加载本地库:JVM的类加载器无法加载本地库,因此可以通过JNI(Java Native Interface)来打破双亲委派模型,直接加载本地库。 3. 使用反射技术:通过反射,可以调用私有的加载类方法,从而绕过双亲委派模型,加载特定的类。 4. 使用SPI机制:在Java标准库中,有一些接口通过SPI(Service Provider Interface)机制提供了灵活的类加载方式,可以通过实现自己的SPI来打破双亲委派模型。 需要注意的是,打破双亲委派模型可能会导致类的重复加载和类冲突,引发程序的运行问题。因此,在使用这些方法时,需要谨慎考虑,确保安全性和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值