0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

类隔离实现之自定义类加载器的扩展

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-08 15:17 次阅读

1、前言

JVM内部架构包含类加载器、内存区域、执行引擎等。日常开发中,我们编写的java文件被编译成class文件后,jvm会进行加载并运行使用类。本次仅对JVM加载部分进行分析,了解并掌握加载机制。

2、类加载是什么?

类加载是一种过程,是将class文件加载到jvm内存的过程。当代码逻辑中需要引用类时,通过类加载器加载引用类对象并存放堆中,以供代码调用。

3、类加载过程

注:类加载过程包含 加载、链接(验证、准备、解析)、初始化

3.1 加载

加载:将类的class字节码文件读到内存,将其存放到运行时数据区的方法区,然后在堆区生成class对象,封装类在方法区内的数据结构。(方法区-》数据结构,堆区-》class对象)

过程:java文件-》通过java c编译成字节码.class文件-》引导类加载器(装载核心类库)-》扩展类加载器(将指定目录jar包装载至工作库)-》系统类加载器(将指定目录的类和jar包装载至工作库,常用)-》自定义类加载器(实现加载指定类或自定义加密等操作)

缓存:类加载到jvm后,会缓存一段时间(不管是否被引用),待jvm执行垃圾回收时才会回收未使用的缓存类,释放空间。

类加载器:

  • 启动类加载器:Bootstrap ClassLoader由C/C++实现,嵌套在JVM中,java程序无法直接操作;负责加载Java核心类库($JAVA_HOME中jre/lib目录下或-Xbootclasspath参数指定的路径目录下,如java.*开头的类)的class文件。
  • 扩展类加载器:Extension ClassLoader由Java编写,由sun.misc.Launcher$ExtClassLoader实现。加载java平台扩展的jar包,负责加载(java.ext.dirs目录或$JAVA_HOMEjre/lib/ext目录,如javax.开头的类)的class文件。
  • 应用程序类加载器:Application ClassLoader由Java编写,由sun.misc.Launcher$AppClassLoader实现。负责加载用户类路径(classpath)的class文件,java程序一般默认使用应用程序类加载器。
  • 自定义类加载器:一般情况下java程序使用上面三种类加载器就满足了,一些特殊情况下,我们需要自定义加载指定路径的类时,就需要继承java.lang.ClassLoader类,重写find Class或loadClass均可实现。(类隔离实践中就采用此方案)

类加载机制

  • 全盘负责:当加载器加载某个class时,该class所引用的其他class也一并被加载(自定义加载class除外);
  • 缓存机制:所有加载过的class均被缓存,当程序中使用某个class时,优先从缓存区中获取,如果缓存区不存在,才会读取该class的字节码文件,加载为class对象,并存入缓存区,以便后续使用。(修改class后,需要重启jvm才会生效)
  • 双亲委派:是一种类加载安全机制,当类加载器需要加载某个class文件时,会优先把加载委托给父类加载器处理,如果加载成功则返回,否则继续向上委托直至最顶层类加载器,当父类加载器在加载范围内均没有找到所需class文件,即表示无法完成加载,此时子加载器才会去加载。(先向上委托父类加载器处理,都失败后在自己再加载)
  • 反向委派:主要是用于第三方包加载,第三方包的类不在jdk/lib目录,所以Bootstrap ClassLoader引导类加载器无法直接加载SPI(Service Provider Interface,服务提供者接口)的实现类,双亲委派机制中定义无法反向委托Application Classloader系统加载器加载,因此需要一种特殊的ContextClassLoader线程上下文类加载器来加载第三方的类库。(*** 此处SPI接口后续文章分析 ***)

加载实现方式

/*
  * 类加载方式
  * 1、类加载器,此方式加载的class对象还没有完成链接阶段
  * 2、java.lang.Class,此方式加载的class对象是完成初始化的
  * */
 ClassLoader classLoader = ClassSegregationTest.class.getClassLoader();
 classLoader.loadClass("com.lgy.example.class_segregation.SegregationTestA");
 // 默认初始化class对象
 Class.forName("com.lgy.example.class_segregation.SegregationTestA");
 // 默认不初始化,并且指定类加载器进行加载
 Class.forName("com.lgy.example.class_segregation.SegregationTestA", false, classLoader);

3.2 链接

链接是将java二进制代码合并至jvm运行的过程。

链接过程可分为 验证、准备、解析 三个阶段。

验证

  • 保证正确加载类,包括文件格式验证(Class文件格式的规范)、元数据验证(Java语言规范)、字节码验证(通过数据流和控制流分析)、符号引用验证。

准备

  • 在方法区为静态变量(static修饰)分配内存,并设置类变量初始值(通常是数据类型默认的零值,如0,0L,null,false等)。
  • 显示赋值是在类对象实例化时处理(即 public static int x=10,准备阶段初始值为0,在对象实例化时,才被赋值10)

解析

  • 虚拟机中将常量池的符号引用(常量名)替换为直接引用(目标的指针地址)的过程;
  • 符号引用的目标不一定在内存中,但常量名(或称字面量)是明确定义在jvm规范的class文件格式中。
  • 直接引用是指向目标的指针地址、相对偏移量或间接定位到目标的句柄,是肯定在内存中。

3.3 初始化

执行每个类的构造方法init()的过程,init()方法是java编译器自动收集、合并所有类变量的赋值动作和静态代码块语句,完成初始化。

初始化步骤

  • 类未被加载或链接,则程序先加载并链接该类
  • 优先初始化直接父类,再执行子类初始化
  • 依次执行类中的初始化语句

初始化条件(只有对类主动使用时才会初始化类)

  • 创建类实例(new Class)
  • 类或接口静态变量的引用或赋值
  • 类静态方法的调用
  • 反射加载(Class.forName(''))
  • 子类被初始化,其父类也会被初始化
  • jvm启动时被标记启动类的类,或直接java.exe命令运行指定类

演示代码如下:

/**
 * 定义父类与子类
 */
 class Parent {
  public static int a = 10;
  static {
   System.out.println(" 父类初始化 ");
  }
 }
 class Children extends Parent{
  public static int a = 100;
  static {
   System.out.println(" 子类初始化 ");
  }
 }
 public static void main(String[] args) throws Exception {
  // 子类没有定义变量a ( public static int a = 100;)
    System.out.println(Children.a); // 输出 --  父类初始化 -- 10 
    // 主动调用时才会执行类的静态块
    -----------------------------------------
    // 子类定义变量a 
    System.out.println(Children.a); // 输出 -- 父类初始化 -- 子类初始化  -- 100 
  // 子类被初始化时,优先初始化父类,所以父类静态块执行;调用变量a属于子类定义,属于主动调用,所以子类静态块执行
 }
  • 调试输出加载对象( VM options 中添加 -XX:+TraceClassLoading
    • [Loaded com.lgy.example.class_segregation.Parent from file:/E:/dataway-demo/example/target/classes/]
    • [Loaded com.lgy.example.class_segregation.Children from file:/E:/dataway-demo/example/target/classes/]
  • 仅在首次主动使用才会被初始化。

4、总结

以上就是关于自定义类加载器、加载过程的全部内容。

本文是针对于类隔离实现之自定义类加载器的扩展,对于应用中类加载阶段的进一步分析。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 缓存
    +关注

    关注

    1

    文章

    220

    浏览量

    26443
  • 文件
    +关注

    关注

    1

    文章

    540

    浏览量

    24402
  • 编译
    +关注

    关注

    0

    文章

    615

    浏览量

    32397
  • JVM
    JVM
    +关注

    关注

    0

    文章

    152

    浏览量

    12129
  • 类加载器
    +关注

    关注

    0

    文章

    6

    浏览量

    898
收藏 人收藏

    评论

    相关推荐

    为labview创建自定义探针

    通过自定义探针来访问需要访问的LabVIEW成员vi信息,废话不说直接写原理过程:1. 创建一个demo.Lvproj2. Write data为成员vi3.上图为main.vi4. 右键探针创建
    发表于 03-22 10:32

    CYW54907上的USB主机自定义

    我正在尝试在CYW54 907无线SoC(CYW943907AEVAL1F DEV板)上实现自定义的USB 2主机。我想把主机连接到一个在FX3模块上运行的USB设备上的自定义
    发表于 10-22 14:45

    DLNN:基于(sklearn自带手写数字图片识别数据集)+自定义NN(三层64→100→10)实现975%准确率

    DLNN:基于(sklearn自带手写数字图片识别数据集)+自定义NN(三层64→100→10)实现975%准确率
    发表于 12-21 10:46

    MLHierarchicalClustering:自定义HierarchicalClustering层次聚算法

    MLHierarchicalClustering:自定义HierarchicalClustering层次聚算法
    发表于 12-25 14:54

    Labview 怎么实现自定义

    各位大神们不好意思啊,因为本人菜鸟一个,最近遇到的问题确实是有点多而且频繁,所以我又来了提问了,嘻嘻。。。。。1.在Labview安装目录user.lib里面添加一些自己定义的东西是怎么做到了?图A就是添加自定义文件后的目录,图B是原来的。
    发表于 03-21 09:27

    TP5自定义异常的处理方法

    ) : void }可以用自定义的异常处理扩展 PHP 内置的异常处理,使用自定义
    发表于 09-20 09:05

    如何自定义Component 属性

    ,而是利用现有的API(例如,各种get,set方法)。===如果您想自定义组件,那么需要新创建一个,并继承Component实现其基本的构造方法。然后,在其
    发表于 12-21 09:31

    加载机制的过程和策略

    负责加载环境变量ClassPath指定的库,如果在应用程序中没有自定义加载,一般情况下作为
    发表于 01-05 17:21

    USB自定义设备实现

    2021.5.13(2021.5.17改)USB自定义设备实现1 此例程在GD官方所提供的打印机设备类型修改而来,根据USB2.0协议修改相关的设备描述符、配置描述符和端口描述符,来实现
    发表于 02-22 07:02

    Springboot是如何获取自定义异常并进行返回的

    这里看到新服务是封装的自定义异常,准备入手剖析一下,自定义的异常是如何进行抓住我们请求的方法的异常,并进行封装返回到。废话不多说,先看看如何才能实现封装异常,先来一个示例:在这里,您会看到新服务是一
    发表于 03-22 14:15

    MSCUSB设备如何使用自定义名称描述符进行配置?

    我已将 USB 设备设置为 MSC ,连接到 SD 卡,因此一旦我连接到 PC,我就可以通过 Windows 资源管理访问 SD。它显示为通用 USB 设备。如何使用自定义名称描述符进行配置?谢谢
    发表于 12-26 08:31

    OpenHarmony应用开发自定义弹窗

    本文转载自《OpenHarmony应用开发自定义弹窗》,作者:zhushangyuan_ ​ 应用场景 在应用的使用和开发中,弹窗是一个很常见的场景,自定义弹窗又因为极高的自由度得以广泛应用。本文
    发表于 09-06 14:40

    1602自定义字符

    1602液晶能够显示自定义字符,能够根据读者的具体情况显示自定义字符。
    发表于 01-20 15:43 1次下载

    如何在LabVIEW中实现自定义控件

    本文档的主要内容详细介绍的是如何在LabVIEW中实现自定义控件。
    发表于 01-14 17:17 48次下载
    如何在LabVIEW中<b class='flag-5'>实现</b><b class='flag-5'>自定义</b>控件

    自定义视图组件教程案例

    自定义组件 1.自定义组件-particles(粒子效果) 2.自定义组件- pulse(脉冲button效果) 3.自定义组件-progress(progress效果) 4.
    发表于 04-08 10:48 14次下载