Java虚拟机系列: ClassLoader类加载机制

简介: 改不完的 Bug,写不完的矫情。公众号 杨正友 现在专注移动基础开发 ,涵盖音视频和 APM,信息安全等各个知识领域;只做全网最 Geek 的公众号,欢迎您的关注!

1681522584610.png'

改不完的 Bug,写不完的矫情。公众号 杨正友 现在专注移动基础开发 ,涵盖音视频和 APM,信息安全等各个知识领域;只做全网最 Geek 的公众号,欢迎您的关注!

什么是类加载?

  类加载是一个将.class字节码文件实例化成Class对象并进行相关初始化过程。

说说类的加载过程?

  类加载过程中,JVM会初始化继承树还没有被初始化过所有父类,并且会执行这个链路上所有未执行过的静态代码块,静态变量赋值语句等。某些类在使用时,也可以按需由类加载器进行加载。

Java 的类加载器是一个运行时核心基础设施模块,主要在启动进行类的Load,LinkInit ,即加载,链接,初始化。

  第一步, Load 阶段读取类文件产生二进制流,并转化特定数据结构,初步校验 cafe babe 魔法数,常量池,文件长度,是否有父类等。然后创建对应的java.lang.Class实例。

     第二步, Link阶段包括校验,准备解析三个步骤.校验是更为详细的校验.比如:final 是否合规,类型是否正确,静态变量是否合理等。

准备阶段是为静态变量分配内存,并设置默认值,解析类和方法确保类与类之间相互引用的正确性,完成内存的结构布局。

      第三步, Init 阶段执行类构造器 方法,如果赋值运算是通过其他类的静态方法完成的,那么会马上解析另一个类,在虚拟机栈中执行完毕后返回值进行赋值

1681522705045.png

ClassLoader的作用是啥?

   ClassLoader 的使用是提前加载.class 类文件存在内存中。

ClassLoader加载类涉及的模型?

1681522766737.png

双亲委托派模型。低层次的当前类加载器,不能覆盖高层次类加载器已经加载的类。如果低层次的类加载器想加载一个未知的类,要非常礼貌的向上逐步询问:请问,这个类已经加载过了吗?"被询问的高层次类加载器会问两个问题:第一次我是否一家在过这个类,如果没有,是否可以加载此类?如果党所偶高层次类加载器的两个问题上大的答案均为否时候,才可以让当前类加载器加载该未知类.知道顶层类加载器BootStrapClassLoader

小写 class 和 大写 Class 有啥区别?

   全小写的class是关键字,用来定义类,而首字母大写的Class,它是所有class的类。这句话理解起来有难度

 public class ClassTest {
    // 数组类型有一个魔法属性: length 来获取数组长度
    private static int[] array = new int[3];
    private static int length = array.length;
    // 任何小写 `class` 定义的类,也有一个魔法属性: class,来获取此类的大写 `Class` 类对象
    private static Class<One> one = One.class;
    private static Class<Another> another = Another.class;
    public static void main(String[] args) {
        // 通过newInstance 方法创建 One 和 Another 的类引用(第一处)
        try {
            final One oneObj = one.getDeclaredConstructor().newInstance();
            oneObj.call();
            // 通过 one 这个大写的Class对象,获取私有成员属性对象Filed(第二处)
            final Another anotherObj = another.newInstance();
            anotherObj.speak();
            final Field inner = one.getDeclaredField("inner");
            inner.setAccessible(true);
            inner.set(oneObj, "world changed.");
            // 成功修改类的私有属性 inner 变量值 为world changed
            System.out.println(oneObj.getInner());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    class One {
        private String inner = "time files";
        public void call() {
            System.out.println("hello world.");
        }
        public String getInner() {
            return inner;
        }
    }
    class Another {
        public void speak() {
            System.out.println("easy coding");
        }
    }
}

newnewInstance 的区别?

   new 是强类型校验,可以调用任何构造方法,在使用new的时候,这个类可以没被加载过,而Class类的newInstance是弱类型,只能调用无参数构造方法,如果没有默认构造方法,就抛InstantiationException异常;

如果此构造方法没有权限访问,则抛出IllegalAcessExecption异常。Java通过类加载器把类的实现与类的定义进行解耦,所以是实现面向接口编程,依赖倒置的必然选择。

private 成员在类外是否可以修改?

   通过inner.setAccessible(true)操作,即可使用Class类的set方法修改其值.如果没有就会抛出IllegalAcessExecption

你知道的类加载器有哪些?

   类加载器有着严格的等级制度,最高一层是家族威望最高的Bootstrap,它是在jvm启动时创建的,通常由于操作系统相关本地代码实现,负责装载最核心的Java类,比如:

  • Object,System,String

   第二层是在JDK9版本中,称为 Platform ClassLoader 即为平台类加载器,用以加载一些扩展的系统类,如:

XML,加密,压缩相关类

JDK9 之前的加载器是 Extention ClassLoader;


   第三层是 Application ClassLoader 的应用类加载器,主要市价在用户定义的CLASSPATH路径的类。

  • 第二,第三层类加载器为Java语言实现,用户也可以自定义类加载器。

怎样解决类冲突或jar冲突?

在同一个工程内引用多个框架时,往往被迫进行类仲裁.按照某种规则jar包的版本被统一指定,导致应用程序出现异常,主流的类框架都会自定义类加载器,实现不同中间件的类隔离,避免了类冲突。

怎样防止源码泄漏?

   Java代码容易被编译或篡改,可以进行编译加密。那么类加载器也需要自定义,还原加密字节码。

如何自定义类加载器?

   自定义类加载器的步骤有如下:

  • extends ClassLoader
  • 重写 findClass()
  • 调用definedClass()方法
public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        final byte[] bytes = getClassFromCustomPath(name);
        if (bytes == null){
            throw new FileNotFoundException();
        }else {
            return defineClass(name,bytes,0,bytes.length);
        }
        throw new ClassNotFoundException();
    }
    private byte [] getClassFromCustomPath(String name){
        // 自定义路径中加载类
    }
    public static void main(String [] args){
        final CustomClassLoader customClassLoader = new CustomClassLoader();
        try {
            final Class<?> one = Class.forName("One", true, customClassLoader);
            final Object o = one.newInstance();
            System.out.println(o.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

如何想在App启动的时候观察加载了哪个jar包中的哪个类?

增加 -XX: + TracerClasLoading 参数,此参数在解决类冲突时非常实用,毕竟不同JVM环境对于加载类的顺序并非一致的。

有时想观察特定类加载上下文,由于加载数量众多,调试的时候很难捕捉类的加载过程,这时候可以使用条件断点功能

在使用ClassLoder进行类加载过程,有没有哪些地方违背了双亲委派模型?

   在本地类加载的方式一般我们是这么使用的:

// 正在使用的类加载器:AppClassLoader
ClassLoader c = MicroKibaco.class.getClassLoader();
// AppClassLoader 的父加载器 PlatformClassLoader
ClassLoader c1 =  c.getParent();
// PlatformClassLoader 的父加载器是BootStrap.它是c++实现的,返回null
ClassLoader c2 = c1.getParent();

最高一层BootStrap是通过 c++ 实现的,并不存在JVM体系内,所以输出为null,类加载具有等级制度,但是并非继承关系,以组合方式复用父加载器的功能,这也符合组合有限原则。

 但是双亲委派模型并不是强制必须的,对于自己加载不了的类怎么办?直接用线程上下文类加载器完成,通过:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

这条语句获取本地线程然后实现上下类加载。 所以这个地方 Bootstrap Classloader加载器拿到了 Application ,ClassLoader 加载器应该加载的类,就打破了双亲委派模型。

为什么要用双亲委派模型?好处是什么?

   防止内存中出现多份同样的字节码文件,没法保证类的唯一性,而且如果不使用双亲委派模型,会给JVM虚拟机带来安全隐患,所以要让类的比较有意义前提是被同一个ClassLoader进行加载。

参考资料


相关文章
|
8月前
|
安全 Java
对 JVM 的类加载机制以及寻找字节码文件的“双亲委派模型”的理解
对 JVM 的类加载机制以及寻找字节码文件的“双亲委派模型”的理解
49 0
|
8月前
|
安全 Java 程序员
学习Java类加载机制
在Java的世界里,每一个类或者接口,在经历编译器后,都会生成一个个.class文件。
59 0
|
27天前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
3月前
|
存储 Java C语言
【JVM】类加载机制
【JVM】类加载机制
32 1
|
5月前
|
存储 Java 数据库连接
Java类文件结构及类加载机制
该文章主要讨论了Java类文件的结构以及Java类的加载机制,并提到了双亲委派模型的相关内容。
Java类文件结构及类加载机制
|
5月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
86 0
|
5月前
|
安全 前端开发 Java
【JVM 探秘】ClassLoader 类加载器:揭秘 Java 类加载机制背后的秘密武器!
【8月更文挑战第25天】本文全面介绍了Java虚拟机(JVM)中的类加载器,它是JVM的核心组件之一,负责将Java类加载到运行环境中。文章首先概述了类加载器的基本工作原理及其遵循的双亲委派模型,确保了核心类库的安全与稳定。接着详细阐述了启动、扩展和应用三种主要类加载器的层次结构。并通过一个自定义类加载器的例子展示了如何从特定目录加载类。此外,还介绍了类加载器的完整生命周期,包括加载、链接和初始化三个阶段。最后强调了类加载器在版本隔离、安全性和灵活性方面的重要作用。深入理解类加载器对于掌握JVM内部机制至关重要。
198 0
|
6月前
|
Java 程序员 开发者
Java中的类加载机制详解
Java中的类加载机制详解
|
7月前
|
存储 前端开发 Java
深入解析Java类加载机制:原理、过程与实践
深入解析Java类加载机制:原理、过程与实践
247 2
|
6月前
|
前端开发 Java 数据安全/隐私保护
深入理解Java中的类加载机制
深入理解Java中的类加载机制

热门文章

最新文章