双亲委派机制
为了避免同一个类加载多次,对类的加载机制采用双亲委派模型。
在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;
}
}
}