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,即被卸载了。