美文网首页
命名空间与类的卸载

命名空间与类的卸载

作者: shz_Minato | 来源:发表于2019-02-16 16:15 被阅读1次

命名空间介绍

 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成。也就是说,命名空间就是一个空间,空间里就是可以所有可以加载到的类。
 命名空间的性质如下:
  ①在同一个命名空间中,不会出现类的二进制名字相同的两个类。
  ②在不同的命名空间中,有可能会出现类的二进制名字相同的两个类。
 实例如下:
  验证性质一

public class MyTest extends ClassLoader {
    private String classLoaderName;
    private String fileExtension = ".class";
    private String path;

    public MyTest(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

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

    public MyTest(ClassLoader parent, String classLoaderName) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass invoke: "+name);
        System.out.println("class loader name: "+classLoaderName);
        byte[] classData = getClassData(name);
        return defineClass(null, classData, 0, classData.length);
    }

    public byte[] getClassData(String className) {
        byte[] bytes = null;
        InputStream is = null;
        ByteArrayOutputStream bos = null;

        className=className.replaceAll("\\.","\\\\");
        try {

            File file=new File(this.path,className+fileExtension);

            is = new FileInputStream(file);
            bos = new ByteArrayOutputStream();

            int ch = 0;
            while ((ch = is.read()) != -1) {
                bos.write(ch);
            }

            bytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

   
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        MyTest myTest=new MyTest("MyLoader");
        myTest.setPath("桌面路径");
        //使用加载器myTest加载的类
        Class<?> loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
        Object instance = loadClass.newInstance();
        System.out.println(loadClass.hashCode);
        System.out.println(instance);
        
        System.out.println("------");
        
        //将myTest1加载器的父加载器设置为myTest
        MyTest myTest1=new MyTest(myTest,"MyLoader1");
        myTest1.setPath("桌面路径");
        //使用加载器myTest1加载的类
        Class<?> loadClass1 = myTest1.loadClass("com.minato.jvm.chapter15.People");
        Object instance1 = loadClass.newInstance();
        System.out.println(loadClass1.hashCode);
        System.out.println(instance1);
    }
}

打印结果
findClass invoke: com.minato.jvm.chapter15.People
class loader name: MyLoader //调用了myTest的findClass方法
21685669 //myTest加载器加载出的类对象的hashCode
com.minato.jvm.chapter15.People@7f31245a
------
21685669 //myTest1加载器加载出的类对象的hashCode
com.minato.jvm.chapter15.People@6d6f6e28

两个hashCode值相同,并且findClass方法只执行了一次,证明了加载流程执行了一次。

类加载器的执行顺序
    调用findLoadedClass方法检查欲加载的类是否已经加载
    调用父加载器的loadClass,如果父加载器为空,那么就会用系统类加载加载
    调用findClass去find类

按照双亲委托模型,加载器myTest1当执行findLoadedClass时,会返回true,
后续的顺序并不会执行,因此执行流程只执行一次,返回的值是已经加载的Class,
因此两次hashCode值相同。

  验证性质二

public class MyTest extends ClassLoader {
    private String classLoaderName;
    private String fileExtension = ".class";
    private String path;

    public MyTest(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

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

    public MyTest(ClassLoader parent, String classLoaderName) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass invoke: "+name);
        System.out.println("class loader name: "+classLoaderName);
        byte[] classData = getClassData(name);
        return defineClass(null, classData, 0, classData.length);
    }

    public byte[] getClassData(String className) {
        byte[] bytes = null;
        InputStream is = null;
        ByteArrayOutputStream bos = null;

        className=className.replaceAll("\\.","\\\\");
        try {

            File file=new File(this.path,className+fileExtension);

            is = new FileInputStream(file);
            bos = new ByteArrayOutputStream();

            int ch = 0;
            while ((ch = is.read()) != -1) {
                bos.write(ch);
            }

            bytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

   
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        MyTest myTest=new MyTest("MyLoader");
        myTest.setPath("桌面路径");
        //使用加载器myTest加载的类
        Class<?> loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
        Object instance = loadClass.newInstance();
        System.out.println(loadClass.hashCode);
        System.out.println(instance);
        
        System.out.println("------");
        
        MyTest myTest1=new MyTest("MyLoader1");
        myTest1.setPath("桌面路径");
        //使用加载器myTest1加载的类
        Class<?> loadClass1 = myTest1.loadClass("com.minato.jvm.chapter15.People");
        Object instance1 = loadClass.newInstance();
        System.out.println(loadClass1.hashCode);
        System.out.println(instance1);
    }
}

打印结果
findClass invoke: com.minato.jvm.chapter15.People 
class loader name: MyLoader // 调用了加载器myTest的findClass方法
21685669 //加载出的类的hashCode
com.minato.jvm.chapter15.People@7f31245a //创建出的实例
------
findClass invoke: com.minato.jvm.chapter15.People
class loader name: MyLoader1 // 调用了加载器myTest1的findClass方法
1173230247 //加载出的类对象的hashCode
com.minato.jvm.chapter15.People@330bedb4

以上的加载器myTest和myTest1是两个不同的类加载器,也就是说有两个不用命名空间,
因此完整的走了两次加载流程,hashCode的值不同

类的卸载

 由JVM自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。JVM自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。JVM本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类Class对象,因此这些Class对象始终是可触及的。

    以上的关键是 JVM本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类Class对象

 由用户自定义的类加载器所加载的类是可以被卸载的,因为JVM不会始终保持自定义类加载器的引用。
 实例
  配置JVM参数 -XX:+TraceClassUnloading 追踪类的卸载

    方法是 将引用断开
    public class MyTest extends ClassLoader {
    private String classLoaderName;
    private String fileExtension = ".class";
    private String path;

    public MyTest(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

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

    public MyTest(ClassLoader parent, String classLoaderName) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass invoke: " + name);
        System.out.println("class loader name: " + classLoaderName);
        byte[] classData = getClassData(name);
        return defineClass(null, classData, 0, classData.length);
    }

    public byte[] getClassData(String className) {
        byte[] bytes = null;
        InputStream is = null;
        ByteArrayOutputStream bos = null;

        className = className.replaceAll("\\.", "\\\\");
        try {

            File file = new File(this.path, className + fileExtension);

            is = new FileInputStream(file);
            bos = new ByteArrayOutputStream();

            int ch = 0;
            while ((ch = is.read()) != -1) {
                bos.write(ch);
            }

            bytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    public static void test(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter15.People");
        Object instance = loadClass.newInstance();
        System.out.println(instance.getClass().getClassLoader());
        System.out.println(instance);
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        MyTest myTest = new MyTest("MyLoader");
        myTest.setPath("桌面路径");
        //使用加载器myTest加载的类
        Class<?> loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
        Object instance = loadClass.newInstance();
        System.out.println("------");

        //引用断开
        myTest = null;
        loadClass = null;
        instance = null;
        //回收  生命周期终止
        System.gc();

        myTest = new MyTest("MyLoader");
        myTest.setPath("桌面路径");
        //使用加载器myTest加载的类
        loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
        instance = loadClass.newInstance();
        System.out.println("------");
    }
}

结果如下:
findClass invoke: com.minato.jvm.chapter15.People
class loader name: MyLoader
------
[Unloading class com.minato.jvm.chapter15.People //类被卸载 0x0000000100061828]
findClass invoke: com.minato.jvm.chapter15.People //累被重新加载
class loader name: MyLoader
------

相关文章

  • 命名空间与类的卸载

    命名空间介绍  每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成。也就是说,命名空...

  • 浅谈Python类命名空间

    Python的类就像命名空间。Python程序默认处于全局命名空间内,类体则处于类命名空间内,Python 允许在...

  • 06_JVM学习笔记_类的命名空间、类的卸载

    类的命名空间 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。 在同一个命名空间中...

  • C#基础第九天(面向对象)

    1、命名空间 可以认为类是属于命名空间的。 如果在当前项目中没有这个类的命名空间,需要我们手动导入这个类所在的命名...

  • C 语言的命名空间

    C语言也有命名空间,可分位4类命名空间。 4类命名空间: 所有的标签(label)都属于同一个命名空间。说明:①在...

  • 1.命名空间+类自动加载+yii2+psr-4标准

    1.命名空间 PHP 命名空间可以解决以下两类问题:1) 用户编写的代码与PHP内部的类/函数/常量或第三方类/函...

  • TypeScript基础入门之声明合并(三)

    转发 TypeScript基础入门之声明合并(三) 声明合并 将命名空间与类,函数和枚举合并 命名空间足够灵活,...

  • 【深入理解Java虚拟机 】类加载器的命名空间以及类的卸载

    类加载器的命名空间 每个类加载器又有一个命名空间,由其以及其父加载器组成 类加载器的命名空间的作用和影响 每个类加...

  • ThinkPHP 命名规范

    命名规范: 目录与文件: 目录 小写+下划线; 类库和函数文件以 .php为后缀; 类的文件名以命名空间定义,命名...

  • 09. 类加载器命名空间

    命名空间 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成 在同一个命名空间中,不会...

网友评论

      本文标题:命名空间与类的卸载

      本文链接:https://www.haomeiwen.com/subject/abfieqtx.html