jvm双亲委派及其破坏

简介: jvm双亲委派及其破坏

jvm双亲委派及其破坏

类的生命周期

类的生命周期

首先可以从图中明确类的生命周期

1)遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类型没有进行过初始 化,则需要先触发其初始化阶段。能够生成这四条指令的典型Java代码场景有: ·使用new关键字实例化对象的时候。 ·读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外) 的时候。·调用一个类型的静态方法的时候。

2)使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需 要先触发其初始化。

3)当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先 初始化这个主类。

5)当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解 析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句 柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。

6)当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有 这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

  1. 验证

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

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

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

  1. 加载

1)文件格式验证

2)元数据验证

3)字节码验证

4)符号引用验证

  1. 准备

    为静态变量分配内存并设置类变量初始值(null)

    注意这里有个概念为方法区,是逻辑概念

    jdk8以前使用永久代实现方法区(有专门的永久代区域),在后面使用了元空间实现方法区,但实例变量物理存储地址其实是放到了堆中。所以说静态变量、字符串等存在堆中也对,存在方法区也对。

  2. 解析

    解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7

    类符号引用进行,分别对应于常量池的CONSTANT_Class_info、CON-STANT_Fieldref_info、

    CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、

    CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dyna-mic_info和 CONSTANT_InvokeDynamic_info 8种常量类型。

    主要有以下4种解析过程。

    1)类或接口的解析

    2)字段解析

    3)方法解析

    4)接口方法解析

  3. 初始化
  4. 使用
  5. 卸载

jdk8双亲委派模型

JDK8类加载器

双亲委派核心代码package sun.misc.Launcher;

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (var2) {
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);
            }
        }

从代码和图中可知所谓双亲委派就是一层一层往上找,每层加载的代码不同避免了重复加载,通常用户所写的代码在系统类加载器

演示代码

package com.example.demo.lesson.jvm.loader;

import sun.misc.Launcher;

import java.net.URL;

public class ClassLoaderExe {
    public static void main(String[] args) {
        // 核心rt.jar中的类加载器 是C++加载的,因此这里为null
        System.out.println(String.class.getClassLoader());
        // 扩展包的加载器 ExtClassLoader
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader());
        // 应用加载器 AppClassLoader
        System.out.println(ClassLoaderExe.class.getClassLoader());


        System.out.println("");

        // 获取系统ClassLoader
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        // appClassLoader的父加载器
        ClassLoader extClassLoader = appClassLoader.getParent();
        // extClassLoader的父加载器
        ClassLoader boostrapClassLoader = extClassLoader.getParent();

        System.out.println("the bootstrapLoader : " + boostrapClassLoader);
        System.out.println("the extClassloader : " + extClassLoader);
        System.out.println("the appClassLoader : "+ appClassLoader);

        System.out.println("==============bootstrapLoader加载的文件====================");


        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urLs.length; i++) {
            System.out.println(urLs[i]);
        }
        System.out.println("");


        System.out.println("==============extClassloader加载的文件====================");
        System.out.println(System.getProperty("java.ext.dirs"));

        System.out.println("");


        System.out.println("==============appClassLoader 加载的文件====================");
        System.out.println(System.getProperty("java.class.path"));
    }
}

jdk9破坏双亲委派模型

JDK9类加载器

核心代码在jdk11进行了迁移jdk.internal.loader.ClassLoaders

    private static final BootClassLoader BOOT_LOADER;
    private static final PlatformClassLoader PLATFORM_LOADER;
    private static final AppClassLoader APP_LOADER;

    // Creates the built-in class loaders.
    static {
        // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
        String append = VM.getSavedProperty("jdk.boot.class.path.append");
        BOOT_LOADER =
            new BootClassLoader((append != null && !append.isEmpty())
                ? new URLClassPath(append, true)
                : null);
        PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);

        // A class path is required when no initial module is specified.
        // In this case the class path defaults to "", meaning the current
        // working directory.  When an initial module is specified, on the
        // contrary, we drop this historic interpretation of the empty
        // string and instead treat it as unspecified.
        String cp = System.getProperty("java.class.path");
        if (cp == null || cp.isEmpty()) {
            String initialModuleName = System.getProperty("jdk.module.main");
            cp = (initialModuleName == null) ? "" : null;
        }
        URLClassPath ucp = new URLClassPath(cp, false);
        APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
    }

从图和代码中可知在java模块化后通过判断该模块由哪个类加载器加载就直接加载不用再一层层往上找

演示代码

package com.example.demo.lesson.jvm.loader;


import java.net.URL;

public class ClassLoaderPlant {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        System.out.println(ClassLoaderPlant.class.getClassLoader());


        System.out.println("");

        // 获取系统ClassLoader
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        // appClassLoader的父加载器
        ClassLoader platformClassLoader = appClassLoader.getParent();
        // platformClassLoader的父加载器
        ClassLoader boostrapClassLoader = platformClassLoader.getParent();

        System.out.println("the bootstrapLoader : " + boostrapClassLoader);
        System.out.println("the extClassLoader : "+ platformClassLoader);
        System.out.println("the appClassLoader : "+ appClassLoader);
    }
}

友情链接

目录
可直接运行的完整代码
视频讲解
文字版

如果有帮助到你的话请顺手点个赞,这对我真的很重要

相关文章
|
容器
Thymeleaf $/*/#/@语法
Thymeleaf $/*/#/@语法
424 0
|
Java 开发工具 git
spring源码下载、编译、debug的详细教程
spring源码下载、编译、debug的详细教程
1689 0
spring源码下载、编译、debug的详细教程
|
Rust 算法 Go
【密码学】一文读懂FNV Hash
FNV哈希全名为Fowler-Noll-Vo算法,是以三位发明人Glenn Fowler,Landon Curt Noll,Phong Vo的名字来命名的,最早在1991年提出。它可以快速hash大量的数据并保持较小的冲突概率,适合hash一些相近的字符串比如IP地址、URL、文件名等等。目前FNV算法有三个版本,分别是: FNV-0(已废弃)、FNV-1以及FNV-1a。这三个算法的结构非常相似,因此呢,在这里就一块说了。
4009 0
【密码学】一文读懂FNV Hash
|
小程序 API 数据安全/隐私保护
微信小程序开发中的一些常用标签
这些标签是微信小程序开发中的基础,开发者可以根据需要组合使用这些标签来构建小程序的界面。每个标签都有其属性和事件,可以通过属性来调整组件的样式和行为,通过事件来响应用户的操作。
752 5
|
算法 安全 Java
性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
【4月更文挑战第28天】性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
894 1
性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
|
12月前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
987 60
|
9月前
|
监控 前端开发 Java
SpringBoot集成Tomcat、DispatcherServlet
通过这些配置,您可以充分利用 Spring Boot 内置的功能,快速构建和优化您的 Web 应用。
659 21
|
设计模式 Java API
如何处理Java中的方法过长?
如何处理Java中的方法过长?
thinkPhP6.0安装教程图解--PHP框架安装
本文是一篇关于ThinkPHP 6.0安装教程的图解,包括环境检查、安装Composer、修改Composer镜像地址、安装ThinkPHP框架以及启动运行ThinkPHP的步骤。文章详细描述了每个步骤的操作方法,并提供了相应的命令和截图,帮助用户理解并顺利完成ThinkPHP 6.0的安装和运行。
thinkPhP6.0安装教程图解--PHP框架安装
|
监控 前端开发 安全
JVM工作原理与实战(十四):JDK9及之后的类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JDK8及之前的类加载器、JDK9及之后的类加载器等内容。
533 2