总结

  1. 类加载器是负责加载类的对象。类ClassLoader是一个抽象类。给定类的全限定类名,类加载器应尝试查找或生成构成该类定义的数据Class文件。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的类文件
  2. 每个Class对象都包含一个Class.getClassLoader()方法可以获取到定义它的ClassLoader
  3. 数组类的Class对象不是由类加载器创建的,而是根据Java运行时的要求自动创建的。getClassLoader()返回的数组类的类装入器与其元素类型的类装入器相同,如果元素类型是基础类型,则数组类没有类装入器。
  4. 除了加载类之外,类加载器还负责定位资源。资源是一些数据(例如 .class文件、配置数据或图像)。资源通常与应用程序或库一起打包,以便可以通过应用程序或库中的代码找到它们。
  5. ClassLoader类使用委托模型来搜索类和资源。ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例通常会在尝试查找类或资源本身之前,将对该类或资源的搜索委托给其父类装入器。
  6. Java内置类加载器
加载器名 方法名 作用
Bootstrap class loader 虚拟机的内置类加载器,底层是用C 实现的,没有父加载器。主要加载系统环境的一些jar包和.class文件。C/C 语言编写,是虚拟机的一部分,无法在Java代码中直接获取它的引用。可以通过 System.getProperty(“sun.boot.class.path”)获取其加载路径下的文件
Platform class loader (也称为ExtClassLoader) getPlatformClassLoader() 平台类加载器,负责加载JDK中一些特殊的模块。主要加载java.ext.dirs下的.class文件
System class loader (也称为AppClassLoader) getSystemClassLoader() 系统类加载器,负责加载用户类路径上所指定的类库。主要加载java.class.path下的.class文件,是面向用户编写类的类加载器,即自己写的类或者引入的第三方库通常由此加载器加载
  1. 通常Java虚拟机以依赖于平台的方式从本地文件系统加载类。但是,有些类可能不是源于文件;它们可能来自其他来源,如网络,也可能由应用程序构建。 方法defineClass(String name, byte[] b, int off, int len),将字节数组转换为类class的实例。这个新定义的类的实例可以使用Class.newInstance()方法创建
  2. 类加载器创建的对象的方法和构造函数可以引用其他类。为了确定引用的类,Java虚拟机调用最初创建该类的类加载器的loadClass方法

ClassLoader 虚拟类方法

方法名 作用
protected ClassLoader(String name, ClassLoader parent) 创建指定名称name的新类加载器,并使用指定的父类加载器parent进行委派
public String getName() 返回此类加载器的名称,如果此类加载器未命名,则返回null
public Class loadClass(String name, boolean resolve) 加载具有指定类名称的类,resolve为true表示解析类引用。loadClass方法会先调用getClassLoadingLock方法获取锁,再调用findLoadedClass方法检查类是否已加载,如果未加载,则往父加载器一直递归调用loadClass加载该类,如果父加载器也加载不了该类,才调用findClass方法获取Class对象,而findClass是虚拟方法由子类实现。其实现使用defineClass(String name, byte[] b, int off, int len)方法可以将class文件的字节数组转为Class对象,最后使用resolveClass方法进行类的链接。而由于获取该字节数组的方法是很多样的,所以类加载的方式也非常多样,如本地加载、网络加载、压缩包中加载、自己构建Class文件
protected Object getClassLoadingLock(String className) 返回类加载操作的锁对象。 如果此ClassLoader对象注册为支持并行,则该方法返回与指定类名 className关联的专用对象。否则该方法将返回此ClassLoader对象,即同一时间一个ClassLoader只能加载一个类
protected final Class findLoadedClass(String name) 如果Java虚拟机已将此加载程序记录为具有给定全限定类名称的类的初始加载程序,则返回具有给定全限定类名称的类。否则返回 null
protected Class findClass(String name) 查找具有指定全限定类名的类。这个方法是空方法应该被子类重写,并且被调用在检查请求类的父类加载器之后,loadClass方法将调用这个方法
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) 将字节数组转换为具有给定保护域ProtectionDomain的类Class的实例。 如果指定的name以“java.”开头,它只能由getPlatformClassLoader()获取到的平台类加载器或其祖先定义(define),否则将抛出SecurityException。如果name不是null,则它必须等于字节数组b指定的类的全限定类名称,否则将抛出NoClassDefFoundError
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b, ProtectionDomain protectionDomain) 将字节缓冲区ByteBuffer转换为具有给定保护域ProtectionDomain的类Class的实例。其余和上面一样
protected final void resolveClass(Class c) 链接指定的类。类加载器可能会使用此方法链接类。如果类c已经被链接,那么这个方法直接返回。 否则,将按照Java语言规范执行一章中的描述链接该类

实现代码热替换

通过上面我们可以知道类加载流程是

  1. loadClass方法会先调用getClassLoadingLock方法获取锁
  2. 再调用findLoadedClass方法检查类是否已加载,如果已经加载则直接获取到该Class类对象,再判断该Class类对象是否需要链接(resolve),如果要链接进入resolveClass,链接完后直接返回。链接就是执行类加载过程中的验证、准备、解析这些过程
  3. 如果未加载,则往父加载器一直递归调用loadClass加载该类
  4. 如果父加载器也加载不了该类,才调用findClass方法获取Class对象而findClass是空方法由子类重写
  5. 其实现中会使用defineClass(String name, byte[] b, int off, int len)方法,该可以将class文件的字节数组转为Class对象
  6. 如果需要链接,最后使用resolveClass方法进行类的链接

实现

  1. 如果我们要实现代码热替换,那么就要使用defineClass方法加载新的类,所以最简单的实现就是直接使用defineClass方法将新的Class文件字节数组转为Class对象,再使用反射创建新对象并执行新方法
  2. 但是defineClass是protected方法,所以我们只能继承ClassLoader虚拟类才能调用该方法
  3. 最好的规范就是继承ClassLoader虚拟类,并实现其loadClass方法和findClass方法,并在loadClass方法中调用findClass方法,在findClass方法中再调用defineClass方法
  4. 为了便于理解,直接抛弃规范,直接自己写一个方法直接调用defineClass方法实现代码热替换

项目结构,out是编译出的class文件目录,由于Test就在src目录下,没有包名,则其全限定类名为Test

public class Test extends ClassLoader {
    public static void main(String[] args) throws Exception {
        while(true) {
            try {
                Test test = new Test();
                // 编译后的class文件位置 ./表示代码根目录
                String classFile = "./out/production/Java_hot_replace/Test.class";
                FileInputStream fis = new FileInputStream(classFile);
                byte[] bytes = new byte[1024*10];
                int len = fis.read(bytes);
                //将字节数组转为Class类对象 Test为全限定类名
                Class clazz = test.defineClass("Test", bytes, 0 ,len);
                //使用反射根据新的Class对象创建新对象,并执行其printStr方法
                Object object = clazz.newInstance();
                Method m = object.getClass().getMethod("printStr", new Class[] {});
                m.invoke(object, new Object[] {});
                Thread.sleep(2000);
            } catch(Exception e) {
                e.printStackTrace();
                try {
                    Thread.sleep(2000);
                } catch(InterruptedException ex) {

                }
            }
        }
    }

    public void printStr() {
        System.out.println("A");
    }
}

启动后修改代码,然后点重新编译

可以看到代码被热替换了

改进思考

  • 正常实现流程应该为继承ClassLoader虚拟类,并重写其loadClass方法和findClass方法,并在loadClass方法中调用findClass方法,在findClass方法中再调用defineClass方法
  • 实现热替换应该是替换修改过的代码,则应当维护一个Map<String, Long> 存储从全限定类名到上次文件修改时间的映射,每次定时扫描Class文件目录或检测到保存快捷键Ctrl s时触发扫描,文件的属性也有上次修改时间,拿我们存储的和文件的属性比较即可知道文件是否修改,即是否需要重新加载Class类
  • 热替换产生了大量类信息都存储在jdk1.7的永久代,jdk1.8的元空间,如果无用的类信息过多则会造成OOM,我们自定义类加载器和其产生的Class类对象,都可以通过置空(= null)使其不可达,然后调用System.gc()就可以卸载,即类似如下代码
public class Test extends ClassLoader {
   public static void main(String[] args) throws Exception {
     	MyClassLoader classLoader = new MyClassLoader();
 	 	Class classLoaded = classLoader.loadClass("MyClass");
  	 	classLoaded = null; 
  		classLoader = null; 
  	 	System.gc();  
  	}
}

到此这篇关于Java ClassLoader虚拟类实现代码热替换的示例代码的文章就介绍到这了,更多相关Java ClassLoader代码热替换内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java ClassLoader虚拟类实现代码热替换的示例代码的更多相关文章

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  3. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  4. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  5. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  6. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  7. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  8. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  9. Java一维数组和二维数组元素默认初始化值的判断方式

    这篇文章主要介绍了Java一维数组和二维数组元素默认初始化值的判断方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. java实现emqx设备上下线监听详解

    这篇文章主要为大家介绍了java实现emqx设备上下线监听详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部