JVM-2

作者: 小鸡在路上 | 来源:发表于2019-08-07 23:06 被阅读0次

命名空间的补充说明

  1. 关于命名空间的补充说明

    1. 子加载器所加载的类可以访问父加载器所加载的类
    2. 父加载器所加载的类无法访问子加载器所加载的类
    3. 在jvm中是可以存在多个命名空间的,每个命名空间之间是相互隔离开的。命名空间里面的类时相互不可见的。
    4. 在运行期,一个java类是由该类的完全限定名(binary name,二进制名)和用于加载该类的定义类加载器(defining loader)所共同决定的。如果同样的名字(即相同的完全限定名)的类是由两个不同的加载器所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从相同的位置加载亦如此。
  2. 类加载器双亲委托模型的好处

    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类空间,这类技术在很多框架中都得到了实际应用
  3. 先有鸡还是先有蛋问题

    1. 我们知道对于加载器也是java类,那么加载器又是由谁来加载的?
    2. 内建于JVM的启动类加载器会加载java.lang.ClassLoader以及其他的java平台类。当JVM启动时,一块特殊的机器码会运行,他会加载扩展类加载器与系统类加载器 这块特殊的机器码就叫做启动类加载器(bootstrap)启动类加载器并不是java类,而其他的加载器则是java类。启动类加载器是特定于平台的机器指令,他负责开启整个加载过程。所有类加载器(除了启动类加载器)都被实现为java类,不过总归要有一个组件来加载第一个java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯java类加载器就是启动类加载器的职责。启动类加载器还会负责加载供JRE,正常运行所需的基础组件,这包括java.util与java.lang包中的类等等
  4. 线程上下文类加载器分析和实现

    1. 线程上下文类加载器

      • 当前类加载器(Current Classloader) 用于加载当前类的类加载器
      • 每一个类都会使用自己的类加载器(即加载自身的类加载器)来加载其他类(指的是这个类所依赖的其他类)如果ClassX引用了ClassY,那么ClassX的类加载器就会去加载ClassY 前提的ClassY尚未被加载
      • 线程上下文类加载器是从JDK1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别用于获取和设置上下文类加载器。
      • 如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文类加载器。java应用运行时的初始线程的上下文类加载器,在线程中运行的代码可以通过该类加载器来加载该类与资源。
    2. 线程上下文类加载器的重要性

      • SPI(Service Provider Interface)服务提供者接口 面临父加载器看不到子加载器的类的问题父ClassLoader可以使用当前线程Thread.currentThread().getContextLoader()所指定的classloader加载的类这就改变了父ClassLoader不能使用子ClassLoader或是其他没有直接父子关系的ClassLoader加载的类的情况,即改变了双亲委托模型。
      • 线程上下文类加载器就是当前线程的Current Classloader
      • 在双亲委托模型下,类加载时由下至上的,即下层的类加载器会委托上层进行加载,但是对于SPI来说,有些接口是由java核心库说提供的,而java核心库是由启动类加载器来加载的,而这些接口的实现却来自于不同的jar包(厂商提供),java启动类加载器是不会加载其他来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求,而通过给当前线程设置上下类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载。
    3. 线程上下类类加载的一般使用模式(获取 - 使用 - 还原)

      1. 伪代码:

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            try {
                // targetTccl 你准备使用的类加载器
                Thread.currentThread().setContextClassLoader(targetTccl);
                // myMethod里面则调用Thread.currentThread().getContextClassLoader(),获取当前线程的上下文类加载器做某些事情
                myMethod();
            } finally {
                Thread.currentThread().setContextClassLoader(classLoader);
            }
            // 如果一个类由类加载器A加载,那么这个类的依赖类也是由相同的类加载器加载的(如果该依赖类之前没有被加载过的话)
            // ContextClassLoader的作用就是为了破坏java的类加载的委托机制
            // 当高层提供了统一的接口让低层去实现,同时又要在高层加载(或实例化)低层的类时,就必须要通过现场上下文类加载器来帮助高层的ClassLoader找到并加载该类
        
        

相关文章

  • JVM-2

    命名空间的补充说明 关于命名空间的补充说明子加载器所加载的类可以访问父加载器所加载的类父加载器所加载的类无法访问子...

  • JVM-2:Java内存模型

    一、JMM的必要性 众所周知,数据竞争(Data Racing)在并发编程中是个重要问题。操作系统的很大一部分任务...

  • JVM-2 垃圾收集器和内存分配策略

    哪些内存需要回收?什么时候回收?如何回收? 1 GC如何判定对象“已死”? 1.1 引用计数法 1.2 可达性分析...

网友评论

    本文标题:JVM-2

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