invoke方法详解_【性能调优专题】【Jvm性能调优】【JVM【类加载机制详解】【手写定义类加载器】...

97782c70bb9b16bfe7c67ead87c0b117.png

上一期我们讲启动类、扩展类、应用程序类加载器详解

一日之计始于晨:《性能调优专题-Jvm性能调优-JVM类加载机制详解》​zhuanlan.zhihu.com
8ae68d9820dd0d6406ed23877d909122.png

这一期我们自己手写一个类加载器,如下:

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架构学习路线

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值