侧边栏壁纸
博主头像
爱探索

行动起来,活在当下

  • 累计撰写 43 篇文章
  • 累计创建 12 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

java-类加载机制

jelly
2024-08-08 / 0 评论 / 0 点赞 / 55 阅读 / 0 字

java类加载机制

1 java程序的启动过程

java程序启动并运行过程

  1. 最开始java虚拟机回加载main方法所在的类这个类被称为initial class
  2. jvm调用并执行main方法
  3. 在执行main方法时可能回触加载他类并执行其他方法,直到程序结束

类加载过程在java中从启动到运行无处不在,而且,java中的类加载都是在运行过程中动态加载的,这也是java灵活的原因所以在,在java中所有的类加载都是通过ClassLoader完成的

2 Classloader

2.1 加载过程

ClassLoader加载过程

  1. 触发加载动作,传入类的全限定名称
  2. 使用类加载器ClassLoader获取字节码的二进制流(可以从硬盘,网络等多种途径获取)
  3. 根据字节码二进制流生成Class对象

2.2 java8中几个常见的类加载器

  • AppClassLoader应用类加载器:默认的系统类加载器,它负责加载应用程序中classpath中的类
  • ExtClassLoader扩展库目录加载器:负责加载扩展目录类,扩展目录一般指<java_home>/lib/ext,java9中被PlatformClassLoader所取代
  • BootStrapClassLoader核心类库加载器:加载JDK中的核心类库。例如java8中<JAVA_HOME>/jre/lib目录。一般是使用C/C++来实现的

2.2.1 不同加载器类加载位置设置

  1. AppClassLoader
    • 设置classpath:java- cp D:jelly\java-classloader\taget\classer MainClass
    • 读取ClassPath: System.getProperty("java.class.path")
      2.ExtClassPath
    • 设置扩展库目录:java -Djava.ext.dirs="D:\java\ext" MainClass
    • 读取扩展库目录:System.getProperty("java.ext.dirs")

2.2.1 不同加载器类之间的关系

  • 从jvm角度看分为两种:
    1. BootstrapClassLoader它是jvm的一部分,又C/C++来实现
    2. 另一种是用户自定义的ClassLoader,包括AppClassLoader ExtClassLoader 以及用户自己实现的ClassLoader
  • BootstrapClassLoader、ExtClassLoader、AppClassLoader之间是组合关系,并不是继承关系。AppClassLoader显示的拥有一个parent加载器ExtClassLoader。ExtClassLoader的parent隐式的指向BootstrapClassLoader。

3 ClassLoader的协作(双亲委派)

在说ClassLoader协作之前,先要指出以下几点:

  1. 每一个Class都有对应的ClassLoader
  2. 下面所说的父类不是继承关系中的父类,而是组合关系中的
  3. 每一个ClassLoader都有一个“父类”加载器,Bootstrap ClassLoader除外。
  4. 每个类加载器请求总是有些委派给“父类”加载器去尝试加载。
  5. 对于用户有自定义的类加载器默认“父类”式AppClassLoader

3.1 双亲委派模型

双亲委派模型

  1. 将这个类全限定类名传入类加载器
  2. UserClassLoader将这个请求委派给“父类”
  3. AppClassLoader给“父类”委派
  4. ExtClassLoader“父类”等于null(parent==null),则调用native方法调用
  5. Bootstrap ClassLoader处理请求,如果加载成功则返回加载的Class对象,失败则将请求返回给ExtClassLoader
  6. ExtClassLoader处理请求,如果加载成功则返回加载的Class对象,失败则将请求返回给AppClassLoader
  7. AppClassLoader处理请求,如果加载成功则返回加载的Class对象,失败则将请求返回给UserClassLoader
  8. UserClassLoader处理请求,如果加载成功则返回加载的Class对象,失败则抛出异常

3.2 几个关键方法

  • loadClass(..)
  • defineClass(..)
  • findClass(..)
  • findBootstrapClassOrNull(..)
  • getParent()
  1. loadClass

    protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException

    根据类的全限定名称来加载并创建类对象的入口

    • 如果不需要打破双亲委派模型,则尽量不要重写整个方法
    • 如果需要打破双亲委派模型则必须重写这个方法
  2. defineClass

    protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)

    将字节码的字节流转换成一个Class对象

    • find方法,不能覆盖
    • 最终通过一个native方法,将字节流转换成对象。
  3. findClass

    protected Class<?> findClass(String name) throws ClassNotFoundException

    根据类的全限定名称,获取字节码二进制流,并创建对象

    • 如果遵循双亲委派模型,那么我们通常覆盖整个方法
    • findClass实现逻辑
      • 首先根据参数name,从执行来源获取字节码的二进制流
      • 然后通过调用defineClass方法创建一个Class对象
  4. findBootstrapClassOrNull

    private Class<?> findBootstrapClassOrNull(String name)

    根据全限定名,委派BootStrap ClassLoader进行加载

  5. getParent

    public final ClassLoader getParent()

    获取当前ClassLoader的“父类”加载器

    • 注意:parent字段是private final的
    • parent!=null 时调用parentloadClass将加载请求委派给parent
    • parent == null 时。调用findBootstrapClassOrNull将请求委派给Bootstrap ClassLoader

4 类加载器的特性

4.1 用来确定类的唯一性

N:表示某个类的全限定名
L:加载定义这个类的类加载器

N,L:二元组<N,L>,可以用来确定类的唯一性。

4.2 类加载器的传递性

假设类C是由类加载器L1加载的,那么这个类C中所依赖的其他类也将会通过类加载器L1来进行加载。

4.3 可见性

加载类A的类加载器L,也可以通过委派间接的加载类B。那么说:类B对类A是可见的,类不对类A对类B是不可见的。

5 数组类

在java中所有数组实例都属于Object,每个数组实例都有对应的Class

5.1 数组类的加载

  • 数组类并不通过类加载器来加载创建,而是通过JVM直接加载创建的
  • 数组类的元素类型,如果是引用类型,那么最终靠类加载器去创建
  • 数组类的唯一性,依然需要靠类加载器加以确认
    <N,L>N 是数组类的类名,L是相关的类加载器

5.2 数组类相关联的类加载器

  • 如果数组A的组件类型C是引用类型,那么JVM会将数组类A和加载组件类型C的类加载器关联起来
  • 如果数组A的组件类型C是不是引用类型,那么JVM会将数组类与Bootstrap LoaderClass类加载器关联起来
0

评论区