1, 当某个类被加载, 连接和初始化后, 它的生命周期就开始了. 当代表这个类的 Class 对象不再被引用, 即不可触及时, Class 对象就会结束生命周期, 这个类在方法区内的数据也会被卸载, 从而结束这个类的生命周期.
2, 一个类何时结束生命周期, 取决于代表它的 Class 对象何时结束生命周期.
3, 由 Java 虚拟机自带的类加载器所加载的类, 在虚拟机的生命周期中, 始终不会被卸载. 前面已经介绍过, Java 虚拟机自带的类加载器包括根加载器, 扩展类加载器和系统加载器. Java 虚拟机会始终引用这些类加载器, 而这些类加载器则会始终引用它们所加载的 Class 对象, 因此这些 Class 对象始终是可触及的.
4, 由用户自定义的类加载器所加载的类是可以被卸载的.
5, 运行以上程序时, Sample 类由 loader1 加载. 在类加载器的内部实现中, 用一个 Java 集合来存放所加载类的引用. 另一方面, 一个 Class 对象总是会引用它类加载器, 调用 Class 对象的 getClassLoader() 方法, 就会获得它的类加载器. 由此可见, 代表 Sample 类的 Class 实例与 loader1 之间为双向关联关系.
一个类的实例总是引用代表这个类的 Class 对象. 在 Object 类中定义了 getClass() 方法, 这个方法返回代表对象所属的 Class 对象的引用. 此外, 所有的 Java 类都有一个静态属性 class, 它引用代表这个类的 Class 对象.
6, 类卸载的例子
- public class MyTest161 extends ClassLoader{
- private String className;
- // 目录
- private String path;
- private final String fileExtension = ".class";
- public MyTest161(String classLoadName){
- super(); // 将系统类加载器当做该类加载器的父加载器
- this.className = classLoadName;
- }
- public MyTest161(ClassLoader parent, String classLoadName){
- super(parent); // 显示指定该类加载器的父加载器器
- this.className = classLoadName;
- }
- public void setPath(String path) {
- this.path = path;
- }
- @Override
- public String toString() {
- return "[" + this.className + "]";
- }
- @Override
- protected Class<?> findClass(String clasName) throws ClassNotFoundException {
- System.out.println("findClass invoked:" + clasName);
- System.out.println("class loader name:" + this.className);
- byte[] data = this.loadClassData(clasName);
- return this.defineClass(clasName,data, 0, data.length);
- }
- private byte[] loadClassData(String className){
- InputStream is = null;
- byte[] data = null;
- ByteArrayOutputStream baos = null;
- try{
- className = className.replace(".","//");
- //System.out.println("className:" +this.className);
- is = new FileInputStream(new File(this.path + className + this.fileExtension));
- baos = new ByteArrayOutputStream();
- int ch = 0;
- while ( -1 != (ch = is.read())){
- baos.write(ch);
- }
- data = baos.toByteArray();
- }catch (Exception ex){
- ex.printStackTrace();
- }finally {
- try {
- is.close();
- baos.close();
- }catch (Exception ex){
- ex.printStackTrace();
- }
- }
- return data;
- }
- public static void main(String[] args) throws Exception{
- MyTest161 loader1 = new MyTest161("loader1");
- loader1.setPath("D:/temp/");
- Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); //
- System.out.println("class:" + clazz.hashCode());
- Object object = clazz.newInstance();
- System.out.println(object);
- loader1 = null;
- clazz = null;
- object = null;
- System.gc(); // 垃圾回收. 实际开发不会用
- System.out.println();
- loader1 = new MyTest161("loader1");
- loader1.setPath("D:/temp/");
- clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); //
- System.out.println("class:" + clazz.hashCode());
- object = clazz.newInstance();
- System.out.println(object);
- System.out.println();
- }
- }
设置 JVM 参数 -XX:+TraceClassUnloading
运行结果:
- findClass invoked:com.example.jvm.classloader.MyTest1
- class loader name: loader1
- class:21685669
- [email protected]
- findClass invoked:com.example.jvm.classloader.MyTest1
- class loader name: loader1
- class:1173230247
- [Unloading class com.example.jvm.classloader.MyTest1 0x0000000100061028]
- [email protected]
7, 使用 JVisualVM 查看类的卸载
增加睡眠 1 分钟 (JVisualVM 的使用参考 JVisualVM 监控本地 Java 进程 https://www.cnblogs.com/linlf03/p/10056363.html )
然后使用 JVisualVM 观察, 可以发现已经卸载类的总数为 1
来源: http://www.bubuko.com/infodetail-3091645.html