玩转Java虚拟机(四)

打卡学习JVM,第四天

本人学习过程中所整理的代码,源码地址

- 类的卸载

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

- 命名空间在类加载过程的作用

public class MyCat {

    public MyCat() {
        System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());

        System.out.println("from MyCat:" + MySample.class);
    }
}
public class MySample {

    public MySample() {
        System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());

        new MyCat();
    }
}
public class MyTest17 {
    public static void main(String[] args) throws Exception {
        CustomClassLoader loader1 = new CustomClassLoader("loader1");

        Class<?> clazz = loader1.loadClass("classloader.MySample");
        System.out.println("class:" + clazz.hashCode());
        //如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用,因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class
        Object object = clazz.newInstance();
    }
}

首先我们在classpath路径下删除MySample.class文件,程序运行结果没有抛出异常,具体的加载顺序如下:

  • 加载MySample类->委托给系统类加载器->classpath中找不到MySample.class文件->CustomClassLoader加载MySample类->加载成功
  • 加载MyCat类->CustomClassLoader加载->委托给系统类加载器加载->加载成功

如果删除MyCat.class文件,程序运行会抛出异常,那么造成这种结果的原因是什么呢?具体看下加载顺序就会明白了:

  • 加载MySample类->委托给系统类加载器->加载成功
  • 加载MyCat类->系统类加载器加载->classpath中找不到MyCat.class文件->抛出异常

总结:主要原因在于加载MyCat类的加载器是和MySample的类加载器是一样的,从这里我们又可以衍生出关于命名空间的一个重要结论,

- 关于命名空间的重要说明

  • 子加载器所加载的类能够访问到父加载器所加载的类
  • 父加载器所加载的类无法访问到子加载器所加载的类

- 如何查看加载器的具体路径信息

public class MyTest18 {
    public static void main(String[] args) { 
    	//	根加载器
        System.out.println(System.getProperty("sun.boot.class.path"));
        //扩展类加载器
        System.out.println(System.getProperty("java.ext.dirs"));
        //系统类加载器
        System.out.println(System.getProperty("java.class.path"));
    }
}

- 类加载器的双亲委托模型的好处

  1. 可以确保Java核心库的类型安全:所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object类会被加载到Java虚拟机中;如果这个加载过程是由Java应用自己的类加载器所完成的,那么很可能在JVM中存在多个版本的java.lang.Object类,而且这些类之间还是不兼容的,相互不可见的(命名空间在发挥着作用)。借助于双亲委托机制,Java核心类库中的类的加载工作都是由启动类加载器来统一完成的,从而确保了Java应用所使用的都是同一个版本的Java核心类库,它们之间是互相兼容的
  2. 可以确保Java核心类库所提供的类不会被自定义的类所替代
  3. 不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间,相同名称的类可以并存在Java虚拟机中,子还需要用不同的类加载器来加载他们即可,不同类加载器所加载的类之间是不兼容的,这就相当于在Java虚拟机内部创建了一个又一个相互隔离的Java类空间,这类技术在很多框架中都得到了实际应用

在运行期,一个Java类是由该类的完全限定名(binary name)和用于加载该类的定义类加载器所共同决定的。如果同样名字(完全限定名相同)的类是由两个不同的加载器所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从相同的位置加载亦如此

在Oracle的Hotspot实现中,系统熟悉sun.boot.class.path如果修改错了,则运行会出错,提示如下错误信息

Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object

关于启动类加载器的一些重要知识:在这里插入图片描述

发布了10 篇原创文章 · 获赞 20 · 访问量 961

猜你喜欢

转载自blog.csdn.net/qq_41982594/article/details/104719363
今日推荐