• 双亲委派机制


    双亲委派机制

    为了避免同一个类加载多次,对类的加载机制采用双亲委派模型。

    在java层面的实现:以loadClass的实现,逐步展开

    // 在jdk1.7之前,采用的是全局锁的实现,1.7之后,才有了一个并行加载的实现,其实就是想parallelLockMap中注册,这其实就是锁细粒度化的体现
    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }
    
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先会去检查是否已经加载过了, 如果之前已经加载过了,直接返回class
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 如果没有找到,会调用父加载器取加载,实际类似递归,父类加载器加载的时候,实际也会先去查找
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
    
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
    
                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    // 有没有发现这里,查找类的时候,会调用native方法取查找类加载器中的对象,在java运行时实际是没有存储的,类加载器的实现实际是放在C/C++运行时
    protected final Class<?> findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }
    
    private final native Class<?> findLoadedClass0(String name)
    

    我们通过翻阅JVM的源码去寻找答案:

    JNIEXPORT jclass JNICALL
    Java_java_lang_ClassLoader_findLoadedClass0(JNIEnv *env, jobject loader,
                                               jstring name)
    {
        if (name == NULL) {
            return 0;
        } else {
            return JVM_FindLoadedClass(env, loader, name);
        }
    }
    
    
    JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
      JVMWrapper("JVM_FindLoadedClass");
      
      // 笔者省略了若干不重要的代码, 这里开始查找,注意这里是从SystemDictionary中去查找
      Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
                                                                  h_loader,
                                                                  Handle(),
                                                                  CHECK_NULL);
    
      // 转换为java的对象, 注意JVM中的C运行时在java运行时是不被认识的,中间需要做一层转换
      return (k == NULL) ? NULL :
                (jclass) JNIHandles::make_local(env, k->java_mirror());
    

    注意,上面的核心方法就是从SystemDictionary中去查找已经加载的类。可以猜想一下,在java的运行时,存在多个类加载器,每个类加载器可以加载多个class,整体的实现如图所示:

    其中,很明显,这个存放类的容器就是SystemDictionary。

    // 这里开始通过类名查找InstanceKlass或者ArrayKlass,
    Klass* SystemDictionary::find_instance_or_array_klass(Symbol* class_name,
                                                          Handle class_loader,
                                                          Handle protection_domain,
                                                          TRAPS) {
      Klass* k = NULL;
      assert(class_name != NULL, "class name must be non NULL");
    	
      // 如果是数组执行下面的查找
      if (FieldType::is_array(class_name)) {
        // The name refers to an array.  Parse the name.
        // dimension and object_key in FieldArrayInfo are assigned as a
        // side-effect of this call
        FieldArrayInfo fd;
        BasicType t = FieldType::get_array_info(class_name, fd, CHECK_(NULL));
        if (t != T_OBJECT) {
          k = Universe::typeArrayKlassObj(t);
        } else {
          k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD);
        }
        if (k != NULL) {
          k = k->array_klass_or_null(fd.dimension());
        }
      } else {
        // 不是数组执行执行这里
        k = find(class_name, class_loader, protection_domain, THREAD);
      }
      return k;
    }
    

    怎么判断是否是数组呢? 我们 知道,在字节码中,数组是以"["开头的,

    所以,毫无疑问判断是否是数组,可以用"["来判断。

    // 长度 > 1 , 第一个字符是‘[’, 且合法  
    static bool is_array(Symbol* signature) { return signature->utf8_length() > 1 && signature->byte_at(0) == '[' && is_valid_array_signature(signature); }
    
    

    注:字节码中合法的对象类型有哪些?通常都是以数据类型的首字母表示,但是有几个特殊的:L被对象占用,所以Long->J,B被Byte占用,所以boolean-> Z, 普通对象类型用L表示

    switch(sig->byte_at(i)) {
        case 'B': // T_BYTE
        case 'C': // T_CHAR
        case 'D': // T_DOUBLE
        case 'F': // T_FLOAT
        case 'I': // T_INT
        case 'J': // T_LONG
        case 'S': // T_SHORT
        case 'Z': // T_BOOLEAN
          // If it is an array, the type is the last character
          return (i + 1 == len);
        case 'L':
          // If it is an object, the last character must be a ';'
          return sig->byte_at(len - 1) == ';';
      }
    

    数组的实现比较复杂, 让我们先把上下文切换到普通对象。

    k = find(class_name, class_loader, protection_domain, THREAD);
    
    Klass* SystemDictionary::find(Symbol* class_name,
                                  Handle class_loader,
                                  Handle protection_domain,
                                  TRAPS) {
      // 这里实际就是如果是动态加载的类,直接取父类加载器
      // 会从线程的栈中分配内存存储指针,实际汇编层面存储的是一个地址,及类加载器的地址
      class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
      // Handle  重写了运算符,所以class_loader()取的是内部的class_loader的地址,或者null
      ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());
    
      if (loader_data == NULL) {
        return NULL;
      }
    
      // 根据class_name 和 loader_data计算一个hash值,注意这里实际是放置在一个字典中,
      unsigned int d_hash = dictionary()->compute_hash(class_name, loader_data);
      // 返回hash表中的bucket
      int d_index = dictionary()->hash_to_index(d_hash);
    
      {
        
        No_Safepoint_Verifier nosafepoint;
        // 实际上就是一个hashTable,实际是className+loader_data,计算的hash
        return dictionary()->find(d_index, d_hash, class_name, loader_data,
                                  protection_domain, THREAD);
      }
    }
    

    注意到这里的关键对象是就是dictionary,即容器对象就是dictionary

    Dictionary*            SystemDictionary::_dictionary          = NULL;
    
    // 注意继承体系,见明知意,这个容器就是一个hashTable
    class Dictionary : public TwoOopHashtable<Klass*, mtClass>
    

    从前面的整体代码,可以看出,类加载器的整体存储结构如图所示:

    现在,还有一个点,loader_data_offset的值,将这个值弄清楚就可以拿到loadData。

    //计算偏移量
    void java_lang_ClassLoader::compute_offsets() {
      assert(!offsets_computed, "offsets should be initialized only once");
      offsets_computed = true;
      // 首先parallelCapable的偏移量
      Klass* k1 = SystemDictionary::ClassLoader_klass();
      compute_optional_offset(parallelCapable_offset,
        k1, vmSymbols::parallelCapable_name(), vmSymbols::concurrenthashmap_signature());
    
      // 等价于: _loader_data_offset=JavaClasses::compute_injected_offset(JavaClasses::klass_loader_data_enum);
      // 这里会计算_loader_data_offset的偏移量
      CLASSLOADER_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
    }
    

    细心的读者会发现,Java.lang.ClassLoader中是没有loader_data的偏移量的,这里通过JVM注入进去的。在JVM中也有依赖注入,通过找到parallelLockMap的偏移量,然后在后面添加loader_data, 完成了依赖注入。

    上文中直接略过了数组的实现,让我们把上下文切到数组中,一览数组的实现

      // 通过 "[" 判断是数组
      if (FieldType::is_array(class_name)) {
        FieldArrayInfo fd;
        BasicType t = FieldType::get_array_info(class_name, fd, CHECK_(NULL));
        // 如果不是对象类型,也就是原生数据类型
        if (t != T_OBJECT) {
          k = Universe::typeArrayKlassObj(t);
        } else {
          k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD);
        }
        // 包装一下
        if (k != NULL) {
          k = k->array_klass_or_null(fd.dimension());
        }
      }
    
    BasicType FieldType::get_array_info(Symbol* signature, FieldArrayInfo& fd, TRAPS) {
      
      assert(basic_type(signature) == T_ARRAY, "must be array");
      int index = 1;
      int dim   = 1;
      skip_optional_size(signature, &index);
      // 字节码中,有'['就表示是数组,维度用多个'['表示,如二维数组为'[['
      while (signature->byte_at(index) == '[') {
        index++;
        dim++;
        skip_optional_size(signature, &index);
      }
      ResourceMark rm;
      // 过滤掉前面数组标识
      char *element = signature->as_C_string() + index;
      BasicType element_type = char2type(element[0]);
      // 引用烈性和原生数据类型数组的表示形式有差异
      if (element_type == T_OBJECT) {
        int len = (int)strlen(element);
        assert(element[len-1] == ';', "last char should be a semicolon");
        element[len-1] = '\0';        // chop off semicolon
        fd._object_key = SymbolTable::new_symbol(element + 1, CHECK_(T_BYTE));
      }
     	// 维度
      fd._dimension = dim;
      return element_type;
    }
    

    通过一个简单的示例来说明,原生数据类型为[I, 引用类型为[Ljava.lang.Object;

    从前文的代码中,可以看健,针对原生数据类型的类加载的获取和引用类型是不一样。考虑下,引用类型是用户自定义的类型,类型+所占用的内存空间都不一样, 而原生数据类型,只有8种, 都是固定的。

    if (t != T_OBJECT) {
      k = Universe::typeArrayKlassObj(t);
    } else {
      k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD);
    }
    
    //原生数据类型的处理方式
    static Klass* typeArrayKlassObj(BasicType t) {
      assert((uint)t < T_VOID+1, err_msg("range check for type: %s", type2name(t)));
      assert(_typeArrayKlassObjs[t] != NULL, "domain check");
      return _typeArrayKlassObjs[t];
    }
    
    _boolArrayKlassObj      = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);
    _charArrayKlassObj      = TypeArrayKlass::create_klass(T_CHAR,    sizeof(jchar),    CHECK);
    _singleArrayKlassObj    = TypeArrayKlass::create_klass(T_FLOAT,   sizeof(jfloat),   CHECK);
    _doubleArrayKlassObj    = TypeArrayKlass::create_klass(T_DOUBLE,  sizeof(jdouble),  CHECK);
    _byteArrayKlassObj      = TypeArrayKlass::create_klass(T_BYTE,    sizeof(jbyte),    CHECK);
    _shortArrayKlassObj     = TypeArrayKlass::create_klass(T_SHORT,   sizeof(jshort),   CHECK);
    _intArrayKlassObj       = TypeArrayKlass::create_klass(T_INT,     sizeof(jint),     CHECK);
    _longArrayKlassObj      = TypeArrayKlass::create_klass(T_LONG,    sizeof(jlong),    CHECK);
    
    _typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;
    _typeArrayKlassObjs[T_CHAR]    = _charArrayKlassObj;
    _typeArrayKlassObjs[T_FLOAT]   = _singleArrayKlassObj;
    _typeArrayKlassObjs[T_DOUBLE]  = _doubleArrayKlassObj;
    _typeArrayKlassObjs[T_BYTE]    = _byteArrayKlassObj;
    _typeArrayKlassObjs[T_SHORT]   = _shortArrayKlassObj;
    _typeArrayKlassObjs[T_INT]     = _intArrayKlassObj;
    _typeArrayKlassObjs[T_LONG]    = _longArrayKlassObj;
    
    static inline Klass* create_klass(BasicType type, int scale, TRAPS) {
      TypeArrayKlass* tak = create_klass(type, external_name(type), CHECK_NULL);
      assert(scale == (1 << tak->log2_element_size()), "scale must check out");
      return tak;
    }
    
    TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
                                          const char* name_str, TRAPS) {
      Symbol* sym = NULL;
      if (name_str != NULL) {
        sym = SymbolTable::new_permanent_symbol(name_str, CHECK_NULL);
      }
    
      // 原生数据类型的ClassLoaderData就是根类加载器的数据空间
      ClassLoaderData* null_loader_data = ClassLoaderData::the_null_class_loader_data();
    
      // 在根类的loader_data中去分配空间
      TypeArrayKlass* ak = TypeArrayKlass::allocate(null_loader_data, type, sym, CHECK_NULL);
    
      // 最后将创建的Class添加到loader_data中
      null_loader_data->add_class(ak);
    
      // Call complete_create_array_klass after all instance variables have been initialized.
      complete_create_array_klass(ak, ak->super(), CHECK_NULL);
    
      return ak;
    }
    

    由此,可以得出一个结论原生数据类型的数组是由bootstrap类加载器加载的

    注意,原生数据类型的数组实际创建的是TypeArrayKlass。而引用类型的类加载器实际是InstanceKlass。似乎是拿到了一个Klass,引用类型是取到数组内部所包含的类, 那怎么取的对应的数组呢?

    if (k != NULL) {
      k = k->array_klass_or_null(fd.dimension());
    }
    
    
    Klass* Klass::array_klass_or_null(int rank) {
      EXCEPTION_MARK;
      // No exception can be thrown by array_klass_impl when called with or_null == true.
      // (In anycase, the execption mark will fail if it do so)
      return array_klass_impl(true, rank, THREAD);
    }
    
    // 很明显数组中啥也没做,实际干活的肯定是子类
    Klass* Klass::array_klass_impl(bool or_null, int rank, TRAPS) {
      fatal("array_klass should be dispatched to InstanceKlass, ObjArrayKlass or TypeArrayKlass");
      return NULL;
    }
    
    // InstanceKlass的创建方式
    Klass* InstanceKlass::array_klass_impl(bool or_null, int n, TRAPS) {
      instanceKlassHandle this_oop(THREAD, this);
      return array_klass_impl(this_oop, or_null, n, THREAD);
    }
    
    Klass* InstanceKlass::array_klass_impl(instanceKlassHandle this_oop, bool or_null, int n, TRAPS) {
      if (this_oop->array_klasses() == NULL) {
        if (or_null) return NULL;
    
        ResourceMark rm;
        JavaThread *jt = (JavaThread *)THREAD;
        {
          // Atomic creation of array_klasses
          MutexLocker mc(Compile_lock, THREAD);   // for vtables
          MutexLocker ma(MultiArray_lock, THREAD);
    
          // Check if update has already taken place
          if (this_oop->array_klasses() == NULL) {
            // 实际是创建的一个ObjArrayKlass,并给arrayKlass赋值
            Klass*    k = ObjArrayKlass::allocate_objArray_klass(this_oop->class_loader_data(), 1, this_oop, CHECK_NULL);
            this_oop->set_array_klasses(k);
          }
        }
      }
      // _this will always be set at this point
      ObjArrayKlass* oak = (ObjArrayKlass*)this_oop->array_klasses();
      if (or_null) {
        return oak->array_klass_or_null(n);
      }
      return oak->array_klass(n, CHECK_NULL);
    }
    

    一言以蔽之,所有的数组对应的Class都会创建ObjArrayKlass,实际是跟数据里面包含的元素的类加载器是同一个类加载器所加载,只不过,原生数据类型是在bootstrap中。引用类型持有对应数组的引用,原生数据类型在TypeArrayKlass中持有。

    注意到在类加载的过程中,如果没有父加载器,说明此时它已经是根类加载器了,会直接从Bootstrap加载器中尝试查询class。

    try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            // 从根类加载器中加载
            c = findBootstrapClassOrNull(name);
        }
    } catch (ClassNotFoundException e) {
        // ClassNotFoundException thrown if class not found
        // from the non-null parent class loader
    }
    // 如此这般,最终是到调的native方法
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;
    
        return findBootstrapClass(name);
    }
    // native
    private native Class<?> findBootstrapClass(String name);
    

    那么传说中的根类加载器,到底是个啥,为啥打印的时候会返回null?

    // 返回null
    System.out.println(Class.class.getClassLoader());
    

    我们逐步来揭开它神秘的面纱。细心的读者,从前文中,也指导数组类型的类加载器就是根类加载器,它就是加载器的根节点。

    JNIEXPORT jclass JNICALL
    Java_java_lang_ClassLoader_findBootstrapClass(JNIEnv *env, jobject loader,
                                                  jstring classname)
    {
      
    		// 省略其他无关逻辑
        cls = JVM_FindClassFromBootLoader(env, clname);
    
     done:
        if (clname != buf) {
            free(clname);
        }
    
        return cls;
    }
    
    JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                                  const char* name))
      JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
    	// 去掉了一些无关逻辑,比如校验等
      TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
      // 注意这里是resolve_or_null, findLoadedClass调用的是find_instance_or_array_klass
      Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
      if (k == NULL) {
        return NULL;
      }
    
      if (TraceClassResolution) {
        trace_class_resolution(k);
      }
      return (jclass) JNIHandles::make_local(env, k->java_mirror());
    JVM_END
    

    细心的读者可以翻到前文去回看下,findLoadedClass里面调用的是find_instance_or_array_klass, 而这里调用的是resolve_or_null,很明显前者只会从SystemDictionary中取查找,不会加载,而resolve_or_null会取加载class。

    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, TRAPS) {
    
      // Handle() 内部的对象就是个空的
      return resolve_or_null(class_name, Handle(), Handle(), THREAD);
    }
    
    // 注意这里的参数, class_loader, 如果是bootstrap类加载器,这里传入的是Handle()
    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS);
    

    这里为了简化开发,C++会利用运算符重载的功能来简化对象的使用,试想一下,在JAVA没有运算符的重载,遇到可能存在为空的情况都是一场噩梦。大片大片类似如下的代码:

    if (obj == null) {
      // doSomething()
    } else {
      // doOtherThing()
    }
    // 要避免控制的判断,可以利用装饰者模式,将obj的方法都自己实现一遍。
    

    在C++中,可以利用运算符重载,配合指针完美解决这个问题。在Handle的地址实际和_handle 是同一个地址,可以直接做转换。

    // VALUE_OBJ_CLASS_SPEC只是定义的一个宏,标记为值对象,类似JAVA的标记接口
    class Handle VALUE_OBJ_CLASS_SPEC {
     private:
      oop* _handle;
    
     protected:
      oop     obj() const                            { return _handle == NULL ? (oop)NULL : *_handle; }
      oop     non_null_obj() const                   { assert(_handle != NULL, "resolving NULL handle"); return *_handle; }
    
     public:
      // Constructors
      Handle()                                       { _handle = NULL; }
      Handle(oop obj);
      // 在栈中分配空间存放对象,注意这里实际就是存放的一个地址
      Handle(Thread* thread, oop obj);
    
      // 这里是几个关键运算符的重载, 注意这里没有new的重载,VALUE_OBJ_CLASS_SPEC中对new重载,直接抛出异常
      oop     operator () () const                   { return obj(); }
      oop     operator -> () const                   { return non_null_obj(); }
      bool    operator == (oop o) const              { return obj() == o; }
      bool    operator == (const Handle& h) const          { return obj() == h.obj(); }
    
      // Null checks
      bool    is_null() const                        { return _handle == NULL; }
      bool    not_null() const                       { return _handle != NULL; }
    
      // Debugging
      void    print()                                { obj()->print(); }
    
      Handle(oop *handle, bool dummy)                { _handle = handle; }
      oop* raw_value()                               { return _handle; }
      static oop raw_resolve(oop *handle)            { return handle == NULL ? (oop)NULL : *handle; }
    };
    

    在回到主流程中的代码:

      return resolve_or_null(class_name, Handle(), Handle(), THREAD);
    

    Handle()实际取到内部的_handle为NULL。所以可以得出结论:Bootstrap类加载器实际是不存在,即NULL,跟我们之前的案例,BootLoader是不存在的。

    查看resolve_or_null的实现,很明显数组和普通对象的处理方式会有差异(回忆一下前文,查找的时候,数组和普通的class存放的位置也是不一样,也没道理用同一种方式来处理)。

    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
      assert(!THREAD->is_Compiler_thread(),
             err_msg("can not load classes with compiler thread: class=%s, classloader=%s",
                     class_name->as_C_string(),
                     class_loader.is_null() ? "null" : class_loader->klass()->name()->as_C_string()));
      // 数组类型               
      if (FieldType::is_array(class_name)) {
        return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
        // 其他引用类型,
      } else if (FieldType::is_obj(class_name)) {
        ResourceMark rm(THREAD);
        // Ignore wrapping L and ;.
        // 这里会去除里面包含的"L"表示引用类型,以及末尾的;注意java字节码使用unicode编码,所以一个字符两个字节
        TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                       class_name->utf8_length() - 2, CHECK_NULL);
        return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
      } else {
        // 这里实际处理的是原生数据类型,如I,B,F,D,但是原生数据类型在JVM层面做了特殊处理
        return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
      }
    }
    

    同理,我们先把上下文切到普通类的加载,把目光聚焦在BootLoader上,节class_loader为NULL。这将是一个漫长之旅。笔者会省略若干无关的代码。

    // 原生数据类型和引用类型都会通过这个处理,先看下class_loader是null的情况,不是数组
    Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
                                                            Handle class_loader,
                                                            Handle protection_domain,
                                                            TRAPS) {
      // 简化后的代码,BootLoader返回的一定是NULL,but通过Handle包装了
      class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
      // 根类加载器返回的是
      ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);
    	
      // 从SystemDictionary中取查找,本质上是hash表,所以需要计算hash值
      unsigned int d_hash = dictionary()->compute_hash(name, loader_data);
      int d_index = dictionary()->hash_to_index(d_hash);
      Klass* probe = dictionary()->find(d_index, d_hash, name, loader_data,
                                          protection_domain, THREAD);
      // 如果在字典中查到了,直接返回,说明之前已经加载过了                                    
      if (probe != NULL) return probe;
    
      bool DoObjectLock = true;
      // 你以为java层面才有锁,实际JVM层面也做了下控制
      if (is_parallelCapable(class_loader)) {
        DoObjectLock = false;
      }
    	// 占位的,类似spring容器的多级缓存,解决循环依赖的问题,先占位,先略过这段。笔者省略后面相关的处理
      unsigned int p_hash = placeholders()->compute_hash(name, loader_data);
      int p_index = placeholders()->hash_to_index(p_hash);
      //  跳过N行代码
      bool throw_circularity_error = false;
      if (!class_has_been_loaded) {
        bool load_instance_added = false;
        // 省略若干代码,这里是双端检测 
        // 到这里还没有被加载成功
        if (!class_has_been_loaded) {
          // 那么我们就去执行加载
          k = load_instance_class(name, class_loader, THREAD);   
        }
        if (load_instance_added == true) {	
          MutexLocker mu(SystemDictionary_lock, THREAD);
          // 从占位符中找到并删除
          placeholders()->find_and_remove(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD);
          SystemDictionary_lock->notify_all();
        }
      }
      return k();
    }
    

    关键方法是load_instance_class。

    // 这里先看下bootloader的情况
    instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
      instanceKlassHandle nh = instanceKlassHandle(); // null Handle
      // 如果是根类加载器
      if (class_loader.is_null()) {
        
        instanceKlassHandle k;
    		// 无关代码省略
        if (k.is_null()) {
          PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
          // 加载,实际就是解析class字节码
          k = ClassLoader::load_classfile(class_name, CHECK_(nh));
        }
    
        if (!k.is_null()) {
          // 定义,链接,初始化。
          k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
        }
        return k;
      }
    }
    

    笔者可以考虑下,在java层面也是可以读取字节码,所以JVM处理class需要支持根据类名,也需要支持字节数组。这其实就是load_classfile和defineClass1的实现。从上文中可以知道会读取class, 那他怎么知道需要由我们的bootloader加载,而不是appClassLoader加载呢?我们从源码去追寻答案。

    // 根类加载器加载执行这个方法
    instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
      ResourceMark rm(THREAD);
      stringStream st;
      char* name = st.as_string();
    
      ClassFileStream* stream = NULL;
      int classpath_index = 0;
      {
        PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
                                   ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
                                   PerfClassTraceTime::CLASS_LOAD);
        // 弄清这个实体的来源,才能知道从哪加载
        ClassPathEntry* e = _first_entry;
        while (e != NULL) {
          // 注意这里是读取流,即字节流,猜想下,可以通过跟加载的路径和类名去做匹配
          stream = e->open_stream(name, CHECK_NULL);
          if (stream != NULL) {
            break;
          }
          e = e->next();
          ++classpath_index;
        }
      }
    
      instanceKlassHandle h;
      if (stream != NULL) {
        // 注意这一个类的解析
        ClassFileParser parser(stream);
        // bootloader的loader_data,存放Class的地方
        ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
        Handle protection_domain;
        TempNewSymbol parsed_name = NULL;
        // 这里开始解析字节码
        instanceKlassHandle result = parser.parseClassFile(h_name,
                                                           loader_data,
                                                           protection_domain,
                                                           parsed_name,
                                                           false,
                                                           CHECK_(h));
    
        // add to package table
        if (add_package(name, classpath_index, THREAD)) {
          h = result;
        }
      }
      return h;
    }
    

    _first_entry,来源其实跟bootloader有很大渊源。_first_entry

    // 加载bootstrap的路径
    void ClassLoader::setup_bootstrap_search_path() {
      // 很明显就是classpath的路径
    	char* sys_class_path = os::strdup(Arguments::get_sysclasspath());
      int len = (int)strlen(sys_class_path);
      int end = 0;
      for (int start = 0; start < len; start = end) {
        while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) {
          end++;
        }
        char* path = NEW_C_HEAP_ARRAY(char, end-start+1, mtClass);
        strncpy(path, &sys_class_path[start], end-start);
        path[end-start] = '\0';
        // 开始更新path
        update_class_path_entry_list(path, false);
        FREE_C_HEAP_ARRAY(char, path, mtClass);
        while (sys_class_path[end] == os::path_separator()[0]) {
          end++;
        }
      }
    }
    
    void ClassLoader::update_class_path_entry_list(char *path,
                                                   bool check_for_duplicates) {
      struct stat st;
      if (os::stat(path, &st) == 0) {
        ClassPathEntry* new_entry = NULL;
        Thread* THREAD = Thread::current();
        new_entry = create_class_path_entry(path, &st, LazyBootClassLoader, CHECK);
        if (!check_for_duplicates || !contains_entry(new_entry)) {
          add_to_list(new_entry);
        }
      }
    }
    // 添加到链表中
    void ClassLoader::add_to_list(ClassPathEntry *new_entry) {
      if (new_entry != NULL) {
        if (_last_entry == NULL) {
          _first_entry = _last_entry = new_entry;
        } else {
          _last_entry->set_next(new_entry);
          _last_entry = new_entry;
        }
      }
    }
    
    
  • 相关阅读:
    IE11和传统asp.net的兼容问题
    时区和夏令时
    GTA项目 三, 使用 bootstrap table展示界面,使得data和UI分离
    GTA项目 二, JSON接口开放跨域访问
    GTA项目 一, 包装外部WebService
    DNS域名解析
    CRM 迁移服务器备忘
    CentOS6.5 安装HAProxy 1.5.20
    Custom IFormatProvider
    数据分区考虑
  • 原文地址:https://www.cnblogs.com/dragonfei/p/15987433.html
Copyright © 2020-2023  润新知