在Java中的类加载器一文中介绍过Java中的类加载器以及它们的创建和初始化过程,它们本身之间并没有很强的关系,而且应用类加载器和扩展类加载器是由Java代码实现在JDK中的,而启动类加载器则是通过C++代码实现在Hotspot VM源码中的。而类加载过程则会串联起来这几个类加载器,而这正是本文要分析的内容。
在前面介绍类加载器时就讲到Hotspot VM会调用LauncherHelper类的checkAndLoadMain方法来加载主类。
// 获取系统类加载器,也就是AppClassLoader
private static final ClassLoader scloader = ClassLoader.getSystemClassLoader();
/*
* 检查和加载主类
*/
public static Class<?> checkAndLoadMain(boolean printToStderr,
int mode,
String what) {
// 初始化输出
initOutput(printToStderr);
// get the class name
// 获取类名称
String cn = null;
// 根据启动模式按照不同形式获取主类
switch (mode) {
case LM_CLASS:
cn = what;
break;
case LM_JAR:
// 从jar包中获取主类
cn = getMainClassFromJar(what);
break;
default:
// should never happen
throw new InternalError("" + mode + ": Unknown launch mode");
}
// 将类文件路径中的"/"替换为“.”,将其转为包名的形式
cn = cn.replace('/', '.');
Class<?> mainClass = null;
try {
/*
* 根据主类名称加载主类,这里的scloader是指系统类加载器。
* 一般情况下,这里的scloader也就是应用类加载器。
*/
mainClass = scloader.loadClass(cn);
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
if (System.getProperty("os.name", "").contains("OS X")
&& Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
try {
// On Mac OS X since all names with diacretic symbols are given as decomposed it
// is possible that main class name comes incorrectly from the command line
// and we have to re-compose it
mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
} catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
abort(cnfe, "java.launcher.cls.error1", cn);
}
} else {
abort(cnfe, "java.launcher.cls.error1", cn);
}
}
// set to mainClass
appClass = mainClass;
/*
* Check if FXHelper can launch it using the FX launcher. In an FX app,
* the main class may or may not have a main method, so do this before
* validating the main class.
*/
if (mainClass.equals(FXHelper.class) ||
FXHelper.doesExtendFXApplication(mainClass)) {
// Will abort() if there are problems with the FX runtime
FXHelper.setFXLaunchParameters(what, mode);
return FXHelper.class;
}
// 验证主类
validateMainClass(mainClass);
return mainClass;
}
在该方法中,最重要的是调用了scloader的loadClass方法,该方法中实现了所谓的双亲委派机制。
虽然应用类加载器重写了该方法,但逻辑主要是与安全管理器相关,Java 17中已经移除了安全管理器,所以本文不会介绍,重点关注父类ClassLoader中的实现即可。
双亲委派机制
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/*
* 该方法保证了双亲委派模型,如果自定义类加载器要破坏双亲委派,则重写该方法。
* 如果不破坏,则重写findClass(name)方法。
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 锁住目标类名,避免同一个类被重复加载
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
/*
* 检查类是否已经被当前类加载器加载过了,
* 会调用本地方法findLoadedClass0。
*/
Class<?> c = findLoadedClass(name);
if (c == null) { // 还没被加载过
long t0 = System.nanoTime();
try {
if (parent != null) {
// 先交给父类加载器来加载
c = parent.loadClass(name, false);
} else {
/*
* 如果父类加载器是null,则交给启动类加载器来加载。
* 调用本地方法findBootstrapClass。
*/
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();
/*
* 当前类加载器尝试加载类。
* 该方法是模板方法,由子类实现,具体请参考URLClassLoader。
* 最终会调用到该类中定义的defineClass,然后调用本地方法defineClass1。
*/
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
该方法中,调用findLoadedClass方法来判断类是否已被当前类加载器加载过,如果没有被加载过,则判断当前类加载器是否有父类加载器(组合关系,不是继承关系),如果有则通过父类加载器来加载,如果没有则调用findBootstrapClassOrNull方法来让启动类加载器加载。如果父类加载器没有加载到,再调用findClass方法用当前的类加载器加载。这正是双亲委派机制的体现,即优先让父级类加载器加载,如果父类加载器没有加载到,则当前类加载器才去尝试加载。 最后,如果需要在加载时解析类,则进行类解析;默认是false,所以不会在加载时解析类。
接下来我们分析一下上面三个方法,这三个方法最终都会调用到本地方法,所以下面的分析会从Java代码到C++代码“一镜到底”。
启动类加载器加载类
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
// return null if not found
private native Class<?> findBootstrapClass(String name);
先是调用了checkName方法来判断类的名称是否合法。
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
// return null if not found
private native Class<?> findBootstrapClass(String name);
private boolean checkName(String name) {
if ((name == null) || (name.length() == 0))
return true;
/*
* 类名中不能包含/,如果虚拟机不允许数组语法,那么名称中不能包含字符[
*/
if ((name.indexOf('/') != -1)
|| (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
return false;
return true;
}
接下来就是调用本地方法findBootstrapClass了。
/*
* Returns NULL if class not found.
*/
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_findBootstrapClass(JNIEnv *env, jobject loader,
jstring classname)
{
char *clname;
jclass cls = 0;
char buf[128];
if (classname == NULL) {
return 0;
}
clname = getUTF(env, classname, buf, sizeof(buf));
if (clname == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return NULL;
}
VerifyFixClassname(clname);
// 验证类名
if (!VerifyClassname(clname, JNI_TRUE)) { /* expects slashed name */
goto done;
}
// 查找由启动类加载器加载的类
cls = JVM_FindClassFromBootLoader(env, clname);
done:
if (clname != buf) {
free(clname);
}
return cls;
}
继续调用JVM_FindClassFromBootLoader函数,进入Hotspot VM的实现。
JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
const char* name))
JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
// Java libraries should ensure that name is never null...
// 检查类名是否合法
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
// It's impossible to create this class; the name cannot fit
// into the constant pool.
return NULL;
}
TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
// 查找目标类,没找到会返回NULL
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
if (k == NULL) {
return NULL;
}
if (TraceClassResolution) {
trace_class_resolution(k);
}
// 将Klass实例转换成java.lang.Class对象
return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END
上面函数中调用SystemDictionary的resolve_or_null函数来加载或获取类,返回的是一个表示Java类的C++类型Klass的对象,最后会将该对象转换为java.lang.Class对象。
Klass* SystemDictionary::resolve_or_null(Symbol* class_name, TRAPS) {
/*
* 注意,这里传入的两个Handle都是对null的封装
*/
return resolve_or_null(class_name, Handle(), Handle(), THREAD);
}
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 ;.
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 {
return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
}
}
在这里,按照是普通类还是数组类,调用了不同的方法来加载类。
加载普通类
Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
Handle class_loader,
Handle protection_domain,
TRAPS) {
assert(name != NULL && !FieldType::is_array(name) &&
!FieldType::is_obj(name), "invalid class name");
Ticks class_load_start_time = Ticks::now();
// UseNewReflection
// Fix for 4474172; see evaluation for more details
// 获取类加载器
class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
// 获取类加载器的数据对象
ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);
// Do lookup to see if class already exist and the protection domain
// has the right access
// This call uses find which checks protection domain already matches
// All subsequent calls use find_class, and set has_loaded_class so that
// before we return a result we call out to java to check for valid protection domain
// to allow returning the Klass* and add it to the pd_set if it is valid
// 计算哈希值
unsigned int d_hash = dictionary()->compute_hash(name, loader_data);
// 根据哈希值找到在哈希表中的下标
int d_index = dictionary()->hash_to_index(d_hash);
// 根据下标查找类对应的Klass实例
Klass* probe = dictionary()->find(d_index, d_hash, name, loader_data,
protection_domain, THREAD);
// 如果找到则直接返回
if (probe != NULL) return probe;
// Non-bootstrap class loaders will call out to class loader and
// define via jvm/jni_DefineClass which will acquire the
// class loader object lock to protect against multiple threads
// defining the class in parallel by accident.
// This lock must be acquired here so the waiter will find
// any successful result in the SystemDictionary and not attempt
// the define
// ParallelCapable Classloaders and the bootstrap classloader,
// or all classloaders with UnsyncloadClass do not acquire lock here
bool DoObjectLock = true;
// 是否是并行加载
if (is_parallelCapable(class_loader)) {
DoObjectLock = false;
}
unsigned int p_hash = placeholders()->compute_hash(name, loader_data);
int p_index = placeholders()->hash_to_index(p_hash);
// Class is not in SystemDictionary so we have to do loading.
// Make sure we are synchronized on the class loader before we proceed
// 获取加载器锁对象
Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
check_loader_lock_contention(lockObject, THREAD);
ObjectLocker ol(lockObject, THREAD, DoObjectLock);
// Check again (after locking) if class already exist in SystemDictionary
bool class_has_been_loaded = false;
bool super_load_in_progress = false;
bool havesupername = false;
instanceKlassHandle k;
PlaceholderEntry* placeholder;
Symbol* superclassname = NULL;
{
// 加锁,要考虑到并行加载的情况
MutexLocker mu(SystemDictionary_lock, THREAD);
// 再次查找目标类
Klass* check = find_class(d_index, d_hash, name, loader_data);
if (check != NULL) {
// Klass is already loaded, so just return it
class_has_been_loaded = true;
k = instanceKlassHandle(THREAD, check);
} else {
placeholder = placeholders()->get_entry(p_index, p_hash, name, loader_data);
// 如果父类在加载过程中
if (placeholder && placeholder->super_load_in_progress()) {
super_load_in_progress = true;
// 如果有父类
if (placeholder->havesupername() == true) {
superclassname = placeholder->supername();
havesupername = true;
}
}
}
}
// If the class is in the placeholder table, class loading is in progress
if (super_load_in_progress && havesupername==true) {
// 处理父类加载
k = SystemDictionary::handle_parallel_super_load(name, superclassname,
class_loader, protection_domain, lockObject, THREAD);
if (HAS_PENDING_EXCEPTION) {
return NULL;
}
// 如果父类已经加载完毕
if (!k.is_null()) {
class_has_been_loaded = true;
}
}
// 默认不抛出循环依赖错误
bool throw_circularity_error = false;
/*
* 如果没找到,需要尝试对类进行加载
* 加载时需要考虑的因素较多,包括解决并行加载、触发父类的加载和域权限的验证等
*/
if (!class_has_been_loaded) {
bool load_instance_added = false;
// add placeholder entry to record loading instance class
// Five cases:
// All cases need to prevent modifying bootclasssearchpath
// in parallel with a classload of same classname
// Redefineclasses uses existence of the placeholder for the duration
// of the class load to prevent concurrent redefinition of not completely
// defined classes.
// case 1. traditional classloaders that rely on the classloader object lock
// - no other need for LOAD_INSTANCE
// case 2. traditional classloaders that break the classloader object lock
// as a deadlock workaround. Detection of this case requires that
// this check is done while holding the classloader object lock,
// and that lock is still held when calling classloader's loadClass.
// For these classloaders, we ensure that the first requestor
// completes the load and other requestors wait for completion.
// case 3. UnsyncloadClass - don't use objectLocker
// With this flag, we allow parallel classloading of a
// class/classloader pair
// case4. Bootstrap classloader - don't own objectLocker
// This classloader supports parallelism at the classloader level,
// but only allows a single load of a class/classloader pair.
// No performance benefit and no deadlock issues.
// case 5. parallelCapable user level classloaders - without objectLocker
// Allow parallel classloading of a class/classloader pair
{
// 再次加锁
MutexLocker mu(SystemDictionary_lock, THREAD);
if (class_loader.is_null() || !is_parallelCapable(class_loader)) {
PlaceholderEntry* oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
if (oldprobe) {
// only need check_seen_thread once, not on each loop
// 6341374 java/lang/Instrument with -Xcomp
if (oldprobe->check_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE)) {
throw_circularity_error = true;
} else {
// case 1: traditional: should never see load_in_progress.
while (!class_has_been_loaded && oldprobe && oldprobe->instance_load_in_progress()) {
// case 4: bootstrap classloader: prevent futile classloading,
// wait on first requestor
if (class_loader.is_null()) {
SystemDictionary_lock->wait();
} else {
// case 2: traditional with broken classloader lock. wait on first
// requestor.
double_lock_wait(lockObject, THREAD);
}
// Check if classloading completed while we were waiting
// 查找类
Klass* check = find_class(d_index, d_hash, name, loader_data);
// 找到类
if (check != NULL) {
// Klass is already loaded, so just return it
// 则封装为Klass的handle(句柄)对象
k = instanceKlassHandle(THREAD, check);
class_has_been_loaded = true;
}
// check if other thread failed to load and cleaned up
oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
}
}
}
}
// All cases: add LOAD_INSTANCE holding SystemDictionary_lock
// case 3: UnsyncloadClass || case 5: parallelCapable: allow competing threads to try
// LOAD_INSTANCE in parallel
if (!throw_circularity_error && !class_has_been_loaded) {
PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, NULL, THREAD);
load_instance_added = true;
// For class loaders that do not acquire the classloader object lock,
// if they did not catch another thread holding LOAD_INSTANCE,
// need a check analogous to the acquire ObjectLocker/find_class
// i.e. now that we hold the LOAD_INSTANCE token on loading this class/CL
// one final check if the load has already completed
// class loaders holding the ObjectLock shouldn't find the class here
Klass* check = find_class(d_index, d_hash, name, loader_data);
if (check != NULL) {
// Klass is already loaded, so return it after checking/adding protection domain
k = instanceKlassHandle(THREAD, check);
class_has_been_loaded = true;
}
}
}
// must throw error outside of owning lock
if (throw_circularity_error) {
assert(!HAS_PENDING_EXCEPTION && load_instance_added == false,"circularity error cleanup");
ResourceMark rm(THREAD);
THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string());
}
// 还是没加载到,则再次尝试加载
if (!class_has_been_loaded) {
// Do actual loading
// 加载类
k = load_instance_class(name, class_loader, THREAD);
// For UnsyncloadClass only
// If they got a linkageError, check if a parallel class load succeeded.
// If it did, then for bytecode resolution the specification requires
// that we return the same result we did for the other thread, i.e. the
// successfully loaded InstanceKlass
// Should not get here for classloaders that support parallelism
// with the new cleaner mechanism, even with AllowParallelDefineClass
// Bootstrap goes through here to allow for an extra guarantee check
if (UnsyncloadClass || (class_loader.is_null())) {
if (k.is_null() && HAS_PENDING_EXCEPTION
&& PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
MutexLocker mu(SystemDictionary_lock, THREAD);
Klass* check = find_class(d_index, d_hash, name, loader_data);
if (check != NULL) {
// Klass is already loaded, so just use it
k = instanceKlassHandle(THREAD, check);
CLEAR_PENDING_EXCEPTION;
guarantee((!class_loader.is_null()), "dup definition for bootstrap loader?");
}
}
}
// If everything was OK (no exceptions, no null return value), and
// class_loader is NOT the defining loader, do a little more bookkeeping.
if (!HAS_PENDING_EXCEPTION && !k.is_null() &&
k->class_loader() != class_loader()) {
check_constraints(d_index, d_hash, k, class_loader, false, THREAD);
// Need to check for a PENDING_EXCEPTION again; check_constraints
// can throw and doesn't use the CHECK macro.
if (!HAS_PENDING_EXCEPTION) {
{ // Grabbing the Compile_lock prevents systemDictionary updates
// during compilations.
MutexLocker mu(Compile_lock, THREAD);
// 更新字典
update_dictionary(d_index, d_hash, p_index, p_hash,
k, class_loader, THREAD);
}
if (JvmtiExport::should_post_class_load()) {
Thread *thread = THREAD;
assert(thread->is_Java_thread(), "thread->is_Java_thread()");
JvmtiExport::post_class_load((JavaThread *) thread, k());
}
}
}
} // load_instance_class loop
if (HAS_PENDING_EXCEPTION) {
// An exception, such as OOM could have happened at various places inside
// load_instance_class. We might have partially initialized a shared class
// and need to clean it up.
if (class_loader.is_null()) {
// In some cases k may be null. Let's find the shared class again.
instanceKlassHandle ik(THREAD, find_shared_class(name));
if (ik.not_null()) {
if (ik->class_loader_data() == NULL) {
// We didn't go as far as Klass::restore_unshareable_info(),
// so nothing to clean up.
} else {
Klass *kk;
{
MutexLocker mu(SystemDictionary_lock, THREAD);
kk = find_class(d_index, d_hash, name, ik->class_loader_data());
}
if (kk != NULL) {
// No clean up is needed if the shared class has been entered
// into system dictionary, as load_shared_class() won't be called
// again.
} else {
// This must be done outside of the SystemDictionary_lock to
// avoid deadlock.
//
// Note that Klass::restore_unshareable_info (called via
// load_instance_class above) is also called outside
// of SystemDictionary_lock. Other threads are blocked from
// loading this class because they are waiting on the
// SystemDictionary_lock until this thread removes
// the placeholder below.
//
// This need to be re-thought when parallel-capable non-boot
// classloaders are supported by CDS (today they're not).
clean_up_shared_class(ik, class_loader, THREAD);
}
}
}
}
}
if (load_instance_added == true) {
// clean up placeholder entries for LOAD_INSTANCE success or error
// This brackets the SystemDictionary updates for both defining
// and initiating loaders
MutexLocker mu(SystemDictionary_lock, THREAD);
placeholders()->find_and_remove(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD);
SystemDictionary_lock->notify_all();
}
}
if (HAS_PENDING_EXCEPTION || k.is_null()) {
return NULL;
}
post_class_load_event(class_load_start_time, k, class_loader);
#ifdef ASSERT
{
ClassLoaderData* loader_data = k->class_loader_data();
MutexLocker mu(SystemDictionary_lock, THREAD);
Klass* kk = find_class(name, loader_data);
assert(kk == k(), "should be present in dictionary");
}
#endif
// return if the protection domain in NULL
if (protection_domain() == NULL) return k();
// Check the protection domain has the right access
{
MutexLocker mu(SystemDictionary_lock, THREAD);
// Note that we have an entry, and entries can be deleted only during GC,
// so we cannot allow GC to occur while we're holding this entry.
// We're using a No_Safepoint_Verifier to catch any place where we
// might potentially do a GC at all.
// Dictionary::do_unloading() asserts that classes in SD are only
// unloaded at a safepoint. Anonymous classes are not in SD.
No_Safepoint_Verifier nosafepoint;
if (dictionary()->is_valid_protection_domain(d_index, d_hash, name,
loader_data,
protection_domain)) {
return k();
}
}
// Verify protection domain. If it fails an exception is thrown
validate_protection_domain(k, class_loader, protection_domain, CHECK_NULL);
return k();
}
该方法特别长,而且涉及父类加载以及类的并行加载等机制,但本文重点关注该函数调用到的load_instance_class方法。
启动类加载器加载类
instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
if (class_loader.is_null()) { // 使用引导类加载器加载类
// Search the shared system dictionary for classes preloaded into the
// shared spaces.
instanceKlassHandle k;
{
PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time());
// 在共享字典中搜索预加载的类,默认不使用共享空间,因此查找的结果为NULL
k = load_shared_class(class_name, class_loader, THREAD);
}
// 如果没有找到类
if (k.is_null()) {
// Use VM class loader
PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
/*
* 使用引导类加载器加载类
*/
k = ClassLoader::load_classfile(class_name, CHECK_(nh));
}
// find_or_define_instance_class may return a different InstanceKlass
// 如果找到了类
if (!k.is_null()) {
/*
* 将生成的Klass对象缓存起来
*/
k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
}
return k;
} else { // 使用指定的类加载器加载,最终会调用java.lang.ClassLoader类中的loadClass()方法执行类加载
// Use user specified class loader to load class. Call loadClass operation on class_loader.
ResourceMark rm(THREAD);
assert(THREAD->is_Java_thread(), "must be a JavaThread");
JavaThread* jt = (JavaThread*) THREAD;
PerfClassTraceTime vmtimer(ClassLoader::perf_app_classload_time(),
ClassLoader::perf_app_classload_selftime(),
ClassLoader::perf_app_classload_count(),
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
// Translate to external class name format, i.e., convert '/' chars to '.'
Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));
JavaValue result(T_OBJECT);
KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass());
// Call public unsynchronized loadClass(String) directly for all class loaders
// for parallelCapable class loaders. JDK >=7, loadClass(String, boolean) will
// acquire a class-name based lock rather than the class loader object lock.
// JDK < 7 already acquire the class loader lock in loadClass(String, boolean),
// so the call to loadClassInternal() was not required.
//
// UnsyncloadClass flag means both call loadClass(String) and do
// not acquire the class loader lock even for class loaders that are
// not parallelCapable. This was a risky transitional
// flag for diagnostic purposes only. It is risky to call
// custom class loaders without synchronization.
// WARNING If a custom class loader does NOT synchronizer findClass, or callers of
// findClass, the UnsyncloadClass flag risks unexpected timing bugs in the field.
// Do NOT assume this will be supported in future releases.
//
// Added MustCallLoadClassInternal in case we discover in the field
// a customer that counts on this call
if (MustCallLoadClassInternal && has_loadClassInternal()) {
// 调用java.lang.ClassLoader对象中的loadClass()方法进行类加载
JavaCalls::call_special(&result,
class_loader,
spec_klass,
vmSymbols::loadClassInternal_name(),
vmSymbols::string_class_signature(),
string,
CHECK_(nh));
} else {
JavaCalls::call_virtual(&result,
class_loader,
spec_klass,
vmSymbols::loadClass_name(),
vmSymbols::string_class_signature(),
string,
CHECK_(nh));
}
assert(result.get_type() == T_OBJECT, "just checking");
// 获取调用loadClass()方法返回的java.lang.Class对象
oop obj = (oop) result.get_jobject();
// Primitive classes return null since forName() can not be
// used to obtain any of the Class objects representing primitives or void
// 调用loadClass()方法加载的必须是对象类型
if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
/*
* 获取java.lang.Class对象表示的Java类,也就是获取表示Java类的instanceKlass实例
* 为什么这里获取到后不将Klass对象缓存起来?
*/
instanceKlassHandle k =
instanceKlassHandle(THREAD, java_lang_Class::as_Klass(obj));
// For user defined Java class loaders, check that the name returned is
// the same as that requested. This check is done for the bootstrap
// loader when parsing the class file.
if (class_name == k->name()) {
return k;
}
}
// Class is not found or has the wrong name, return NULL
// 文件不存在或者名称错误
return nh;
}
}
重点关注第一个if代码块中的内容,关键的地方在于两点:
- 调用启动类加载器ClassLoader的load_classfile函数来加载类;
- 调用find_or_define_instance_class来将加载到的类缓存起来;
/*
* 加载类文件
*/
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
ResourceMark rm(THREAD);
EventMark m("loading class %s", h_name->as_C_string());
ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
// 获取类文件名称
stringStream st;
// st.print() uses too much stack space while handling a StackOverflowError
// st.print("%s.class", h_name->as_utf8());
st.print_raw(h_name->as_utf8());
st.print_raw(".class");
char* name = st.as_string();
// Lookup stream for parsing .class file
// 根据文件名称查找class文件
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);
/*
* 从第一个classpath开始遍历所有的类路径。
* 为什么现在的Java应用不用设置classpath,早期版本则需要?
* 或者说Java应用的classpath是怎么被设置的?
*/
ClassPathEntry* e = _first_entry;
while (e != NULL) {
// 从该类路径下获取类文件
stream = e->open_stream(name, CHECK_NULL);
// 如果找到目标类文件,则跳出循环
if (stream != NULL) {
break;
}
// 获取下一个classpath,所有的ClassPathEntry由单链表连接起的
e = e->next();
++classpath_index;
}
}
// 保存目标类的句柄
instanceKlassHandle h;
// 如果找到了文件,则加载并解析
if (stream != NULL) {
// class file found, parse it
ClassFileParser parser(stream);
// 获取引导类加载器对应的ClassLoaderData实例
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
TempNewSymbol parsed_name = NULL;
// 加载并解析class文件,但暂时不连接
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;
}
上面的代码中遍历了ClassPathEntry链表,即从所有能够加载的地方尝试加载,直到加载到目标类。随后调用了parseClassFile函数来解析类文件,这部分篇幅很多,可以单独写很多篇文章了,本文重点关注类加载的逻辑,所以不再深入。
缓存类对象
加载到类后,会将其缓存起来。
// Update system dictionary - done after check_constraint and add_to_hierachy
// have been called.
void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,
int p_index, unsigned int p_hash,
instanceKlassHandle k,
Handle class_loader,
TRAPS) {
// Compile_lock prevents systemDictionary updates during compilations
assert_locked_or_safepoint(Compile_lock);
Symbol* name = k->name();
ClassLoaderData *loader_data = class_loader_data(class_loader);
{
MutexLocker mu1(SystemDictionary_lock, THREAD);
// See whether biased locking is enabled and if so set it for this
// klass.
// Note that this must be done past the last potential blocking
// point / safepoint. We enable biased locking lazily using a
// VM_Operation to iterate the SystemDictionary and installing the
// biasable mark word into each InstanceKlass's prototype header.
// To avoid race conditions where we accidentally miss enabling the
// optimization for one class in the process of being added to the
// dictionary, we must not safepoint after the test of
// BiasedLocking::enabled().
if (UseBiasedLocking && BiasedLocking::enabled()) {
// Set biased locking bit for all loaded classes; it will be
// cleared if revocation occurs too often for this type
// NOTE that we must only do this when the class is initally
// defined, not each time it is referenced from a new class loader
if (k->class_loader() == class_loader()) {
k->set_prototype_header(markOopDesc::biased_locking_prototype());
}
}
// Make a new system dictionary entry.
Klass* sd_check = find_class(d_index, d_hash, name, loader_data);
if (sd_check == NULL) {
// 将Klass对象添加到缓存中
dictionary()->add_klass(name, loader_data, k);
notice_modification();
}
#ifdef ASSERT
sd_check = find_class(d_index, d_hash, name, loader_data);
assert (sd_check != NULL, "should have entry in system dictionary");
// Note: there may be a placeholder entry: for circularity testing
// or for parallel defines
#endif
SystemDictionary_lock->notify_all();
}
}
调用字典对象的add_klass函数来缓存类对象。
void Dictionary::add_klass(Symbol* class_name, ClassLoaderData* loader_data,
KlassHandle obj) {
assert_locked_or_safepoint(SystemDictionary_lock);
assert(obj() != NULL, "adding NULL obj");
assert(obj()->name() == class_name, "sanity check on name");
assert(loader_data != NULL, "Must be non-NULL");
// 计算哈希值
unsigned int hash = compute_hash(class_name, loader_data);
// 计算在哈希桶中的索引
int index = hash_to_index(hash);
// 创建在哈希表中的项
DictionaryEntry* entry = new_entry(hash, obj(), loader_data);
// 添加到哈希表中
add_entry(index, entry);
}
JVM使用的是一个哈希表结构来保存数据,调用了compute_hash函数来计算hash值。
unsigned int compute_hash(Symbol* name, ClassLoaderData* loader_data) {
// 计算目标类名的哈希值
unsigned int name_hash = name->identity_hash();
// loader is null with CDS
assert(loader_data != NULL || UseSharedSpaces || DumpSharedSpaces,
"only allowed with shared spaces");
// 计算类加载器的哈希值
unsigned int loader_hash = loader_data == NULL ? 0 : loader_data->identity_hash();
// 异或两者
return name_hash ^ loader_hash;
}
可以看到,这里同时哈希了类名和类加载器数据。所以对于同一个类文件,使用不同的类加载器来加载,也算是不同的类。
加载数组类
Klass* SystemDictionary::resolve_array_class_or_null(Symbol* class_name,
Handle class_loader,
Handle protection_domain,
TRAPS) {
assert(FieldType::is_array(class_name), "must be array");
Klass* k = NULL;
FieldArrayInfo fd;
// dimension and object_key in FieldArrayInfo are assigned as a side-effect
// of this call
// 获取数组元素的类型
BasicType t = FieldType::get_array_info(class_name, fd, CHECK_NULL);
if (t == T_OBJECT) { // 数组元素的类型为对象
// naked oop "k" is OK here -- we assign back into it
k = SystemDictionary::resolve_instance_class_or_null(fd.object_key(),
class_loader,
protection_domain,
CHECK_NULL);
if (k != NULL) {
k = k->array_klass(fd.dimension(), CHECK_NULL);
}
} else { // 数组元素为Java基本类型
k = Universe::typeArrayKlassObj(t);
k = TypeArrayKlass::cast(k)->array_klass(fd.dimension(), CHECK_NULL);
}
return k;
}
这里又分为两种情况:
- 数组元素是Java基本类型:这里首先是基本类型,然后调用其array_klass函数来获取对应的数组类型。
- 数组元素是引用类型:仍然和加载普通类一样调用resolve_instance_class_or_null函数来加载数组元素的类,上面已经走过一遍代码流程了,不再赘述。然后调用其array_klass函数来获取其数组类型。
具体的数组类型获取过程本文省略。
普通类加载器加载类
JDK层面
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
子类必须实现该方法,应用类加载器和扩展类加载器都继承了URLClassLoader,而该类刚好重写了该方法。
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
// 获取到类文件
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
// 处理类文件
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
}
关键的地方是调用了defineClass方法,接下来会介绍很多和此方法同名的方法,这些方法位于父类中。
private Class<?> defineClass(String name, Resource res) throws IOException {
long t0 = System.nanoTime();
int i = name.lastIndexOf('.');
URL url = res.getCodeSourceURL();
if (i != -1) {
// 获取类的全限定名称,截取.class后缀前面的部分
String pkgname = name.substring(0, i);
// Check if package already loaded.
Manifest man = res.getManifest();
// 获取并验证包
if (getAndVerifyPackage(pkgname, man, url) == null) {
try {
if (man != null) {
definePackage(pkgname, man, url);
} else {
definePackage(pkgname, null, null, null, null, null, null, null);
}
} catch (IllegalArgumentException iae) {
// parallel-capable class loaders: re-verify in case of a
// race condition
if (getAndVerifyPackage(pkgname, man, url) == null) {
// Should never happen
throw new AssertionError("Cannot find package " +
pkgname);
}
}
}
}
// Now read the class bytes and define the class
java.nio.ByteBuffer bb = res.getByteBuffer();
if (bb != null) {
// Use (direct) ByteBuffer:
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
// 加载类
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// must read certificates AFTER reading bytes.
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
// 加载类
return defineClass(name, b, 0, b.length, cs);
}
}
这里调用的两个defineClass方法都位于父类SecureClassLoader中。
protected final Class<?> defineClass(String name,
byte[] b, int off, int len,
CodeSource cs)
{
return defineClass(name, b, off, len, getProtectionDomain(cs));
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
CodeSource cs)
{
// 调用父类ClassLoader的方法
return defineClass(name, b, getProtectionDomain(cs));
}
继续调用父类ClassLoader中的方法。
/*
* 该类中的defineClass实现都是final的,不能被重写。
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
// 类加载前置处理
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
// 加载类
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
// 类加载后置逻辑
postDefineClass(c, protectionDomain);
return c;
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// Use byte[] if not a direct ByteBufer:
if (!b.isDirect()) {
if (b.hasArray()) {
// 调用上面的重载方法
return defineClass(name, b.array(),
b.position() + b.arrayOffset(), len,
protectionDomain);
} else {
// no array, or read-only array
byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer.
// 通过Hotspot VM来加载类
return defineClass(name, tb, 0, len, protectionDomain);
}
}
// 做一些前置操作
protectionDomain = preDefineClass(name, protectionDomain);
// 获取类来源位置
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
// 后置操作
postDefineClass(c, protectionDomain);
return c;
}
private native Class<?> defineClass0(String name, byte[] b, int off, int len,
ProtectionDomain pd);
private native Class<?> defineClass1(String name, byte[] b, int off, int len,
ProtectionDomain pd, String source);
private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
int off, int len, ProtectionDomain pd,
String source);
最后会调用到几个本地方法来让JVM加载类。 这里补充一个问题:为什么我们不能加载我们自定义的以java.开头的类呢?在上面调用本地方法之前,会进行类加载的前置操作。
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
/*
* 从这里可以看出,加载的类的全限定名称不能以java开头
* 《深入理解Java虚拟机(第三版)》的P284页脚注提到的异常就是这里抛出的。
*/
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}
在该方法中,检查了目标类的全限定名是否是以java.开头的,如果是则会抛出异常。以java.开头的类都是Java的核心类,为了安全只能由启动类加载器来加载,Java代码实现的其他类加载器不能加载。
JVM层面
// The existence or signature of this method is not guaranteed since it
// supports a private method. This method will be changed in 1.7.
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
jobject loader,
jstring name,
jbyteArray data,
jint offset,
jint length,
jobject pd)
{
return Java_java_lang_ClassLoader_defineClass1(env, loader, name, data, offset,
length, pd, NULL);
}
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
jobject loader,
jstring name,
jbyteArray data,
jint offset,
jint length,
jobject pd,
jstring source)
{
jbyte *body;
char *utfName;
jclass result = 0;
char buf[128];
char* utfSource;
char sourceBuf[1024];
if (data == NULL) {
JNU_ThrowNullPointerException(env, 0);
return 0;
}
/* Work around 4153825. malloc crashes on Solaris when passed a
* negative size.
*/
if (length < 0) {
JNU_ThrowArrayIndexOutOfBoundsException(env, 0);
return 0;
}
body = (jbyte *)malloc(length);
if (body == 0) {
JNU_ThrowOutOfMemoryError(env, 0);
return 0;
}
(*env)->GetByteArrayRegion(env, data, offset, length, body);
if ((*env)->ExceptionOccurred(env))
goto free_body;
if (name != NULL) {
utfName = getUTF(env, name, buf, sizeof(buf));
if (utfName == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
goto free_body;
}
VerifyFixClassname(utfName);
} else {
utfName = NULL;
}
if (source != NULL) {
utfSource = getUTF(env, source, sourceBuf, sizeof(sourceBuf));
if (utfSource == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
goto free_utfName;
}
} else {
utfSource = NULL;
}
/*
* 加载类
*/
result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);
if (utfSource && utfSource != sourceBuf)
free(utfSource);
free_utfName:
if (utfName && utfName != buf)
free(utfName);
free_body:
free(body);
return result;
}
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass2(JNIEnv *env,
jobject loader,
jstring name,
jobject data,
jint offset,
jint length,
jobject pd,
jstring source)
{
jbyte *body;
char *utfName;
jclass result = 0;
char buf[128];
char* utfSource;
char sourceBuf[1024];
assert(data != NULL); // caller fails if data is null.
assert(length >= 0); // caller passes ByteBuffer.remaining() for length, so never neg.
// caller passes ByteBuffer.position() for offset, and capacity() >= position() + remaining()
assert((*env)->GetDirectBufferCapacity(env, data) >= (offset + length));
body = (*env)->GetDirectBufferAddress(env, data);
if (body == 0) {
JNU_ThrowNullPointerException(env, 0);
return 0;
}
body += offset;
if (name != NULL) {
utfName = getUTF(env, name, buf, sizeof(buf));
if (utfName == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return result;
}
VerifyFixClassname(utfName);
} else {
utfName = NULL;
}
if (source != NULL) {
utfSource = getUTF(env, source, sourceBuf, sizeof(sourceBuf));
if (utfSource == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
goto free_utfName;
}
} else {
utfSource = NULL;
}
// 加载类
result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);
if (utfSource && utfSource != sourceBuf)
free(utfSource);
free_utfName:
if (utfName && utfName != buf)
free(utfName);
return result;
}
三个函数最终都会调用到JVM_DefineClassWithSource函数。
JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source))
JVMWrapper2("JVM_DefineClassWithSource %s", name);
return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD);
JVM_END
// common code for JVM_DefineClass() and JVM_DefineClassWithSource()
// and JVM_DefineClassWithSourceCond()
static jclass jvm_define_class_common(JNIEnv *env, const char *name,
jobject loader, const jbyte *buf,
jsize len, jobject pd, const char *source,
jboolean verify, TRAPS) {
if (source == NULL) source = "__JVM_DefineClass__";
assert(THREAD->is_Java_thread(), "must be a JavaThread");
JavaThread* jt = (JavaThread*) THREAD;
PerfClassTraceTime vmtimer(ClassLoader::perf_define_appclass_time(),
ClassLoader::perf_define_appclass_selftime(),
ClassLoader::perf_define_appclasses(),
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::DEFINE_CLASS);
if (UsePerfData) {
ClassLoader::perf_app_classfile_bytes_read()->inc(len);
}
// Since exceptions can be thrown, class initialization can take place
// if name is NULL no check for class name in .class stream has to be made.
TempNewSymbol class_name = NULL;
if (name != NULL) {
const int str_len = (int)strlen(name);
if (str_len > Symbol::max_length()) {
// It's impossible to create this class; the name cannot fit
// into the constant pool.
THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
}
class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
}
ResourceMark rm(THREAD);
// 准备将类文件转换为文件流
ClassFileStream st((u1*) buf, len, (char *)source);
Handle class_loader (THREAD, JNIHandles::resolve(loader));
if (UsePerfData) {
is_lock_held_by_thread(class_loader,
ClassLoader::sync_JVMDefineClassLockFreeCounter(),
THREAD);
}
Handle protection_domain (THREAD, JNIHandles::resolve(pd));
/*
* 从文件流中解析生成Klass实例
*/
Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader,
protection_domain, &st,
verify != 0,
CHECK_NULL);
if (TraceClassResolution && k != NULL) {
trace_class_resolution(k);
}
// 将Klass对象转为java.lang.Class对象
return (jclass) JNIHandles::make_local(env, k->java_mirror());
}
继续看resolve_from_stream函数。
Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
Handle class_loader,
Handle protection_domain,
ClassFileStream* st,
bool verify,
TRAPS) {
// Classloaders that support parallelism, e.g. bootstrap classloader,
// or all classloaders with UnsyncloadClass do not acquire lock here
bool DoObjectLock = true;
if (is_parallelCapable(class_loader)) {
DoObjectLock = false;
}
ClassLoaderData* loader_data = register_loader(class_loader, CHECK_NULL);
// Make sure we are synchronized on the class loader before we proceed
Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
check_loader_lock_contention(lockObject, THREAD);
ObjectLocker ol(lockObject, THREAD, DoObjectLock);
TempNewSymbol parsed_name = NULL;
// Parse the stream. Note that we do this even though this klass might
// already be present in the SystemDictionary, otherwise we would not
// throw potential ClassFormatErrors.
//
// Note: "name" is updated.
/*
* 解析类文件并生成Klass实例
*/
instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
loader_data,
protection_domain,
parsed_name,
verify,
THREAD);
const char* pkg = "java/";
if (!HAS_PENDING_EXCEPTION &&
!class_loader.is_null() &&
parsed_name != NULL &&
/*
* 这里检查了加载的类的全限定名称不能以java开头,否则报错。
* 其实在JDK类ClassLoader中的preDefineClass方法中就已经检查过了,这里再检查一下。
* 这里的报错信息和JDK类中的报错一样,也是《深入理解Java虚拟机(第三版)》中P284脚注中提到的报错。
*
* 这里有点绕,要使if条件为true,那么strncmp函数返回值就必须为0,也就是类名称以java开头。
*/
!strncmp((const char*)parsed_name->bytes(), pkg, strlen(pkg))) {
// It is illegal to define classes in the "java." package from
// JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
ResourceMark rm(THREAD);
char* name = parsed_name->as_C_string();
char* index = strrchr(name, '/');
*index = '\0'; // chop to just the package name
while ((index = strchr(name, '/')) != NULL) {
*index = '.'; // replace '/' with '.' in package name
}
const char* fmt = "Prohibited package name: %s";
size_t len = strlen(fmt) + strlen(name);
char* message = NEW_RESOURCE_ARRAY(char, len);
jio_snprintf(message, len, fmt, name);
// 抛出异常
Exceptions::_throw_msg(THREAD_AND_LOCATION,
vmSymbols::java_lang_SecurityException(), message);
}
if (!HAS_PENDING_EXCEPTION) {
assert(parsed_name != NULL, "Sanity");
assert(class_name == NULL || class_name == parsed_name, "name mismatch");
// Verification prevents us from creating names with dots in them, this
// asserts that that's the case.
assert(is_internal_format(parsed_name),
"external class name format used internally");
// Add class just loaded
// If a class loader supports parallel classloading handle parallel define requests
// find_or_define_instance_class may return a different InstanceKlass
if (is_parallelCapable(class_loader)) { // 支持并行加载
k = find_or_define_instance_class(class_name, class_loader, k, THREAD);
} else { // 如果不能并行加载,那么直接将Klass实例注册到SystemDictionary中
define_instance_class(k, THREAD);
}
}
// Make sure we have an entry in the SystemDictionary on success
debug_only( {
if (!HAS_PENDING_EXCEPTION) {
assert(parsed_name != NULL, "parsed_name is still null?");
Symbol* h_name = k->name();
ClassLoaderData *defining_loader_data = k->class_loader_data();
MutexLocker mu(SystemDictionary_lock, THREAD);
Klass* check = find_class(parsed_name, loader_data);
assert(check == k(), "should be present in the dictionary");
Klass* check2 = find_class(h_name, defining_loader_data);
assert(check == check2, "name inconsistancy in SystemDictionary");
}
} );
return k();
}
调用了ClassFileParser类的parseClassFile函数来解析类文件,上面介绍启动类加载器加载类时也走到了这个函数。该函数比较复杂,具体的代码实现超过了500行,包含了类文件格式的解析等操作,后面单独写文章来分析。 在上面resolve_from_stream函数中,解析完类文件之后,再次判断了类全限定名是否以java.开头,如果是则抛出异常,因为只有启动类加载器才能加载以java.开头的类。从而做到Java核心类在JDK和JVM层的安全加载。
判断类是否被加载过
这里说的是判断类是否被当前类加载器加载过,即在ClassLoader类的loadClass方法中调用的findLoadedClass方法。
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);
同样地,显示检查了类名,然后调用本地方法。
/*
* 注意这里确实是这里的三个参数和Java类中方法的一个参数不匹配,至于为什么要参考JNI的实现机制。
*/
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_FindLoadedClass函数进入Hotspot VM的实现。
JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
JVMWrapper("JVM_FindLoadedClass");
ResourceMark rm(THREAD);
Handle h_name (THREAD, JNIHandles::resolve_non_null(name));
// 获取类名对应的handle
Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL);
const char* str = java_lang_String::as_utf8_string(string());
// Sanity check, don't expect null
// 检查类名是否为空
if (str == NULL) return NULL;
// 判断类名是否过长
const int str_len = (int)strlen(str);
if (str_len > Symbol::max_length()) {
// It's impossible to create this class; the name cannot fit
// into the constant pool.
return NULL;
}
// 创建一个临时的symbol实例
TempNewSymbol klass_name = SymbolTable::new_symbol(str, str_len, CHECK_NULL);
// Security Note:
// The Java level wrapper will perform the necessary security check allowing
// us to pass the NULL as the initiating class loader.
// 获取类加载器对应的handle
Handle h_loader(THREAD, JNIHandles::resolve(loader));
if (UsePerfData) {
is_lock_held_by_thread(h_loader,
ClassLoader::sync_JVMFindLoadedClassLockFreeCounter(),
THREAD);
}
/*
* 查找目标类是否存在,Hotspot将所有加载的类保存在哈希表中(采用链地址法来解决冲突)
*/
Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
h_loader,
Handle(),
CHECK_NULL);
// 将Klass实例转换成java.lang.Class对象
return (k == NULL) ? NULL :
(jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END
跟进find_instance_or_array_klass函数。
// Look for a loaded instance or array klass by name. Do not do any loading.
// return NULL in case of error.
// 查找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) { // 元素类型为Java基本类型
k = Universe::typeArrayKlassObj(t);
} else { // 元素类型为Java对象
k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD);
}
if (k != NULL) {
// 查找或创建相应维度的ObjArrayKlass实例
k = k->array_klass_or_null(fd.dimension());
}
} else { // 类的查找逻辑
k = find(class_name, class_loader, protection_domain, THREAD);
}
return k;
}
在该函数中,分为是数组还是普通类型两种情况。查找数组类的情况,如果元素是基本类型,那么毫无疑问肯定存在对应类;如果元素是引用类型,主要看该引用类型本身是否存在,所以还是回到了下面的find函数。
查找普通类
Klass* SystemDictionary::find(Symbol* class_name,
Handle class_loader,
Handle protection_domain,
TRAPS) {
// UseNewReflection
// The result of this call should be consistent with the result
// of the call to resolve_instance_class_or_null().
// See evaluation 6790209 and 4474172 for more details.
class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());
if (loader_data == NULL) {
// If the ClassLoaderData has not been setup,
// then the class loader has no entries in the dictionary.
return NULL;
}
// 通过类名和类加载器进行哈希运算
unsigned int d_hash = dictionary()->compute_hash(class_name, loader_data);
// 根据哈希运算的结果进一步计算得到索引,即在哈希表中的下标
int d_index = dictionary()->hash_to_index(d_hash);
{
// Note that we have an entry, and entries can be deleted only during GC,
// so we cannot allow GC to occur while we're holding this entry.
// We're using a No_Safepoint_Verifier to catch any place where we
// might potentially do a GC at all.
// Dictionary::do_unloading() asserts that classes in SD are only
// unloaded at a safepoint. Anonymous classes are not in SD.
No_Safepoint_Verifier nosafepoint;
// 根据索引进行查找
return dictionary()->find(d_index, d_hash, class_name, loader_data,
protection_domain, THREAD);
}
}
有了上面的分析过缓存类的经验,这里逻辑就很简单了,就是根据类名和类加载器数据计算哈希,然后到哈希表中去找。