jvm双亲委派及其破坏

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 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);
    }
}

友情链接

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

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

相关文章
|
7月前
|
存储 Java 编译器
类加载机制和双亲委派机制
类加载机制和双亲委派机制
|
前端开发 安全 Java
JVM类加载和双亲委派机制
JVM类加载和双亲委派机制
139 0
|
10天前
|
Java
类加载器和双亲委派机制
从父类加载器到子类加载器分别为: BootStrapClassLoader 加载路径为:JAVA_HOME/jre/lib ExtensionClassLoader 加载路径为:JAVA_HOME/jre/lib/ext ApplicationClassLoader 加载路径为:classpath 还有一个自定义类加载器
|
4月前
|
Java 编译器
什么是双亲委派机制?
什么是双亲委派机制?
260 59
|
3月前
|
Arthas Java 测试技术
JVM —— 类加载器的分类,双亲委派机制
类加载器的分类,双亲委派机制:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器;JDK8及之前的版本,JDK9之后的版本;什么是双亲委派模型,双亲委派模型的作用,如何打破双亲委派机制
JVM —— 类加载器的分类,双亲委派机制
|
4月前
|
前端开发 Java C++
双亲委派机制
这篇文章详细解释了Java中的双亲委派机制,包括其原理、类加载器的分类(启动类加载器、扩展类加载器、应用程序类加载器)以及它们之间的关系和作用。
|
7月前
|
Java
【JVM】双亲委派机制、打破双亲委派机制
【JVM】双亲委派机制、打破双亲委派机制
56 1
|
7月前
|
前端开发 Java 开发者
JVM类加载器的分类以及双亲委派机制
JVM类加载器的分类以及双亲委派机制
|
Java 关系型数据库 MySQL
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
103 0
|
7月前
|
前端开发 Java API
JVM 类加载器 双亲委派机制
【1月更文挑战第3天】JVM 类加载器 双亲委派机制