您的位置:首页 > 编程语言 > Java开发

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

2017-07-05 21:48 120 查看
目录

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所在的位置为java的classpath,正好符合AppClassLoader的加载规则,则进行加载。需要让Object类不符合AppClassLoader的加载条件。(查看java的classpath,可以通过“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")<
9efd
span style="color:#cc7832;">;
}
}


运行结果: 

分析:

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

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

3、SPI和java热替换

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

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

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

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

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

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

自己理解的,如有错误,欢迎指出。谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  类加载器 java jvm class