双亲委派机制

双亲委派机制

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

在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;
    }
  }
}

posted @ 2022-03-09 22:35  dragonfei  阅读(184)  评论(0编辑  收藏  举报