关于java类加载器的试验和理解

来源:互联网 发布:淘宝无线端短连接 编辑:程序博客网 时间:2024/06/05 18:02

目录

1、如何用自己实现的的类加载器加载自己的类

2、是否可以重复加载同一个类

3、关于SPI和java热替换


==========================================分割线=========================================================


1、实现一个类加载器,并加载一个类

要加载的类Object 

public class Object {}


类加载器MyClassLoader: 

public class MyClassLoader extends ClassLoader {    /**     * Finds the class with the specified <a href="#name">binary name</a>.     * This method should be overridden by class loader implementations that     * follow the delegation model for loading classes, and will be invoked by     * the {@link #loadClass <tt>loadClass</tt>} method after checking the     * parent class loader for the requested class.  The default implementation     * throws a <tt>ClassNotFoundException</tt>.  </p>     *     * @param name The <a href="#name">binary name</a> of the class     * @return The resulting <tt>Class</tt> Object     * @throws ClassNotFoundException If the class could not be found     * @since 1.2     */    @Override    protected Class<?> findClass(String  name) throws ClassNotFoundException {        System.out.println(1);        try {            FileInputStream i = new FileInputStream(new File("/Document/Object.class"));            byte b[] = new byte[i.available()];            i.read(b);            i.close();            return defineClass(name, b, 0, b.length);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}


main函数: 

public class ClassLoaderTest {    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {        MyClassLoader myClassLoader = new MyClassLoader();        myClassLoader.loadClass("Object");        System.out.println(myClassLoader.loadClass("Object").getClassLoader());    }}


全部放到Documents目录下运行:


输入结果展示Object的类加载器为AppClassLoader


分析:

由于我们并没有重写loadClass方法,所有还是双亲委派模型,会去寻找默认的父加载器,即AppClassLoader,因为Object所在的位置为javaclasspath,正好符合AppClassLoader的加载规则,则进行加载。需要让Object类不符合AppClassLoader的加载条件。(查看javaclasspath,可以通过“System.out.println(System.getProperty(“java.class.path”));”获取。)


改进:

Object类放到桌面目录,更改MyClassLoader去桌面加载Object类,其他不动: 

FileInputStream i = new FileInputStream(new File("/Desktop/Object.class"));


运行结果: 


输入结果显示Object的类加载器为MyClassLoader,完成。




2、用自己的类加载器重复加载一个类


main函数更改如下:  

public class ClassLoaderTest {    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {        MyClassLoader myClassLoader = new MyClassLoader();        myClassLoader.findClass("Object");        myClassLoader.findClass("Object");    }}

运行结果: 


分析:

直接调用两次findClass方法(findClass会直接加载Class,自己在classLoader重写的方法),来模拟加载两次的情况。

加载两次会抛出异常,注意不要认为调用两次loadClass没错误,认为jvm允许重复加载类,因为loadClass内部有判断是否已经加载。




3SPIjava热替换


SPIjava定义的接口由最顶层类加载器加载,但是由于实现类一般为第三发实现,所以在引用后,由低于顶层类加载器的类加载,这个时候顶层类加载器无法找到该实现类。

为了解决这个问题,引入了线程上下文类加载器。在通过接口调用实现类方法时,通过该加载器加载类,并调用。

该类加载器,每个线程都是独立的,并且默认为AppClassLoader


热替换在了解后,发现并不是真正的替换原来在jvm永久区使用的Class类,而是用一个新的类加载器加载改动后的该类。然后在以后的使用中,使用这个新生成的Class类。

由于不断的Class类生成,必定会有老旧无用的Class类占永久区的内存空间,不过当该类的实例都被回收,且类没有使用,该类加载器也被回收后,fullGC就会回收Class类。

常见热替换为jsp,每个jsp页面对应一个类加载器,当定时发现jsp有变动后,就新建类加载器加载jsp








自己理解的,如有错误,欢迎指出。谢谢。


原创粉丝点击