【性能调优专题】【Jvm性能调优】【JVM【类加载机制详解】【手写定义类加载器】
程序员文章站
2022-05-06 14:05:18
...
上一期我们讲启动类、扩展类、应用程序类加载器详解
这一期我们自己手写一个类加载器,如下:
package com.example.demo;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author chevy
*/
//继承ClassLoader类,重写findclass方法。
public class MyClassLoader extends ClassLoader {
private String rootPath;
public MyClassLoader(String s) {
rootPath = s;
}
//用于寻找类文件
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
public byte[] loadClassData(String name) {
//name = rootPath + name + ".class";
name = rootPath + name.replace(".", "/") + ".class";
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(new File(name));
out = new ByteArrayOutputStream();
int i = 0;
while ((i = in.read()) != -1) {
out.write(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
}
}
但是在JVM中,即使这个两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的,这是因为不同的ClassLoader实例对象都拥有不同的独立的类名称空间,所以加载的class对象也会存在不同的类名空间中。
但前提是覆写loadclass方法。
在方法第一步会通过Class<?> c = findLoadedClass(name);
从缓存查找,类名完整名称相同则不会再次被加载,因此我们必须绕过缓存查询才能重新加载class对象。当然也可直接调用findClass()方法,这样也避免从缓存查找,如下 :
/**
* 测试我的类加载器
*/
class TestMyClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//创建两个不同的自定义类加载器实例
MyClassLoader myClassLoaderTest1 = new MyClassLoader("/Users/chevy/Projects/PMS/pms_cifi_name/demo/target/classes/");
MyClassLoader myClassLoaderTest2 = new MyClassLoader("/Users/chevy/Projects/PMS/pms_cifi_name/demo/target/classes/");
Class<?> classTest1 = myClassLoaderTest1.findClass("com.example.demo.SayHello");
Class<?> classTest2 = myClassLoaderTest2.findClass("com.example.demo.SayHello");
System.out.println("findClass->class1:" + classTest1.hashCode());
System.out.println("findClass->class2:" + classTest2.hashCode());
//Object o = aClass.newInstance();
//Method sayHello = aClass.getMethod("sayHello", new Class<?>[]{});
//Object invoke = sayHello.invoke(o, new Object[]{});
//System.out.println(invoke);
}
}
/**
*运行结果为
*/
findClass->class1:521645586
findClass->class2:1296064247
如果调用父类的loadClass方法,结果如下,除非重写loadClass()方法去掉缓存查找步骤,不过现在一般都不建议重写loadClass()方法。
/**
* 测试我的类加载器
*/
class TestMyClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//创建两个不同的自定义类加载器实例
MyClassLoader myClassLoaderTest1 = new MyClassLoader("/Users/chevy/Projects/PMS/pms_cifi_name/demo/target/classes/");
MyClassLoader myClassLoaderTest2 = new MyClassLoader("/Users/chevy/Projects/PMS/pms_cifi_name/demo/target/classes/");
Class<?> classTest1 = myClassLoaderTest1.loadClass("com.example.demo.SayHello");
Class<?> classTest2 = myClassLoaderTest2.loadClass("com.example.demo.SayHello");
System.out.println("findClass->class1:" + classTest1.hashCode());
System.out.println("findClass->class2:" + classTest2.hashCode());
//Object o = aClass.newInstance();
//Method sayHello = aClass.getMethod("sayHello", new Class<?>[]{});
//Object invoke = sayHello.invoke(o, new Object[]{});
//System.out.println(invoke);
}
}
/**
*运行结果为
*/
findClass->class1:2128227771
findClass->class2:2128227771
所以如果不从缓存查询相同完全类名的class对象,那么只有ClassLoader的实例对象不同,同一字节码文件创建的class对象自然也不会相同。
单加载器测试最终结果:
package com.example.demo;
/**
* @author chevy
*/
public class SayHello {
public void sayHello() {
System.out.println("Hello,World");
}
}
/**
* 测试我的类加载器
*/
class TestMyClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//创建两个不同的自定义类加载器实例
MyClassLoader myClassLoaderTest = new MyClassLoader("/Users/chevy/Projects/PMS/pms_cifi_name/demo/target/classes/");
Class<?> classTest = myClassLoaderTest.loadClass("com.example.demo.SayHello");
Object o = classTest.newInstance();
Method sayHello = classTest.getMethod("sayHello", new Class<?>[]{});
Object invoke = sayHello.invoke(o, new Object[]{});
}
}
/**
*运行结果为
*/
Hello,World
该章节为:Java架构学习路线-性能调优专题-Jvm性能调优-JVM类加载机制详解-手写定义类加载器。
如果喜欢可以关注该专栏。该专栏讲解整体Java架构学习路线