十四、类的命名空间与卸载详解及jvisualvm使用

本文探讨了Java中类的生命周期,重点讲解了类如何被卸载的过程。当类的Class对象不再被引用时,该类在方法区的数据会被卸载,结束其生命周期。文章通过代码示例展示了自定义类加载器加载的类可以被卸载,而由Java虚拟机自带的类加载器加载的类则不会被卸载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、类的卸载

当一个类被加载、连接、初始化后,他的生命周期就开始了。当代表该类的Class对象不再被引用时,即不可触及时,Class对象就会结束生命周期,该类在方法区的数据也会被卸载,从而结束类的生命周期。
一个类何时结束生命周期,取决于代表他的Class对象何时结束生命周期。
由java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用他们所加载的类的Class对象,因此这些Class对象始终是可触及的。
由用户自定义的类加载器所加载的类是可以被卸载的。

在类加载器的内部实现中,用一个java集合来存放所加载类的引用。另一方面,一个Class对象总是会引用他的类加载器,调用Class对象的getClassLoader()方法,就能获得他的类加载器。因此,Class对象与类加载器之间为双向关联关系。
一个类的实例总是引用代表这个类的Class对象。在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。此外,所有的java类都有一个静态属性class他引用代表这个类的Class对象。

实例:使用jvm参数-XX:+TraceClassUnloading观察类的卸载

package com.jvm.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyTest17 extends ClassLoader {

    private String classLoaderName;

    private String path; // class文件路径

    private final String fileExtension = ".class";

    public MyTest17(String classLoaderName) {
        super(); // 将系统类加载器作为该类加载器的父类加载器
        this.classLoaderName = classLoaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public MyTest17(ClassLoader parent, String classLoaderName) {
        super(parent); // 显式指定该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }


    @Override
    public String toString() {
        return "MyTest17{" +
                "classLoaderName='" + classLoaderName + '\'' +
                '}';
    }


    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass:" + className);
        System.out.println("class loader name:" + this.classLoaderName);
        // 父类中findClass方法返回的是一个异常,所以必须要重写
        byte[] data = this.loadClassData(className);
        return this.defineClass(className, data, 0, data.length);
    }

    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        className = className.replace(".", "/");
        try {
            is = new FileInputStream(new File(this.path + className + this.fileExtension));
            System.out.println(this.path + className + this.fileExtension);
            baos = new ByteArrayOutputStream();
            int ch;
            while (-1 != (ch = is.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return data;
    }


    public static void main(String[] args) throws Exception {
        MyTest17 loader1 = new MyTest17("loader1");
        loader1.setPath("C:/Users/lifeline张/Desktop/");
        Class<?> aClass = loader1.loadClass("com.jvm.classloader.User");
        System.out.println("aClass" + aClass.hashCode());
        System.out.println("===========");
        loader1 = null;
        aClass = null;
        System.gc();

        loader1 = new MyTest17("loader1");
        loader1.setPath("C:/Users/lifeline张/Desktop/");
        aClass = loader1.loadClass("com.jvm.classloader.User");
        System.out.println("aClass" + aClass.hashCode());
    }
}

运行结果:

findClass:com.jvm.classloader.User
class loader name:loader1
C:/Users/lifeline张/Desktop/com/jvm/classloader/User.class
aClass1956725890
===========
[Unloading class com.jvm.classloader.User 0x00000007c0061028]
findClass:com.jvm.classloader.User
class loader name:loader1
C:/Users/lifeline张/Desktop/com/jvm/classloader/User.class
aClass21685669

可以看到,user类被卸载了。

改变main方法为:

 public static void main(String[] args) throws Exception {
        MyTest17 loader1 = new MyTest17("loader1");
        loader1.setPath("C:/Users/lifeline张/Desktop/");
        Class<?> aClass = loader1.loadClass("com.jvm.classloader.User");
        System.out.println("aClass" + aClass.hashCode());
        System.out.println("===========");
        loader1 = null;
        aClass = null;
        System.gc();
        Thread.sleep(20000);
        loader1 = new MyTest17("loader1");
        loader1.setPath("C:/Users/lifeline张/Desktop/");
        aClass = loader1.loadClass("com.jvm.classloader.User");
        System.out.println("aClass" + aClass.hashCode());
    }

在cmd中输入:jvisualvm,打开监视,然后运行main方法:
可以看到:
在这里插入图片描述
已卸载的类的总数为1,即被卸载了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值