Java反射原理
2020-09-08 本文已影响0人
None_Ling
简述
对于Java反射而言 , 会非常耗性能 , 尤其是通过Class.forName
来找到的Class对象. 主要的原理如下 :
- Class.forName
- 通过JNI调用到C层 , 再将类名转换成Descriptor
- 通过Runtime获取ClassLinker对象
- 通过
LookupClass
在boot_class_path
中寻找Class , 找到则返回 - 通过
BootClassLoader
中寻找class , 找到则返回 - 判断当前线程是否允许回调Java层函数 , 如果允许则开始校验描述符规则
- 通过
VMStack.getCallingClassLoader
获取当前ClassLoader , 接着调用ClassLoader.loadClass
返回Class - 更新ClassLoader的ClassTable
- Class.getDeclaredMethods
- 通过
Class
对象找到method_
的值 , 即为方法区的地址 - 通过8bit的大小来分割Method的地址
- 通过
Class.forName
- 在Java层通过
Class.forName
来查找对应的Class
- 如果传入的classLoader为空 , 则会通过
VMStack.getCallingClassLoader
获取ClassLoader
public static Class<?> forName(String name, boolean initialize,ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
// 如果从VMStack中获取ClassLoader失败 , 则从BootClassLoader中查找
loader = BootClassLoader.getInstance();
}
Class<?> result;
try {
// 调用JNI层方法
result = classForName(name, initialize, loader);
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
- 在
/art/runtime/native/java_lang_class.cc
文件中 , 通过Class.forName
方法来获取对应的Class对象
- 将包名换成C层的描述符 :
Lcom/test/test
- 在C层获取ClassLoader的Handle
- 通过ClassLinker->FindClass找到Class指针
static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
// 在ScopedFastNativeObjectAccess中 , 保存了JNIEnv对象以及所在的Thread对象
ScopedFastNativeObjectAccess soa(env);
ScopedUtfChars name(env, javaName);
// 判断字符串是否为空
if (name.c_str() == nullptr) {
return nullptr;
}
// 将com.test.test转换成com/test/test验证二进制className
if (!IsValidBinaryClassName(name.c_str())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
"Invalid name: %s", name.c_str());
return nullptr;
}
// 将com.test.test转换成Lcom/test/test , 生成描述符
std::string descriptor(DotToDescriptor(name.c_str()));
// 从soa.Self中获取JNIEnv所在的线程对象
StackHandleScope<2> hs(soa.Self());
// 获取javaLoader的指针
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
// 从Rumtime.Current中获取ClassLinker对象
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// 通过ClassLinker.findClass找到Class对象 , 并且创建相关Handle
Handle<mirror::Class> c(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
// 如果从class_table与ClassLoader中加载/获取失败的话
if (c == nullptr) {
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
env->ExceptionClear();
jthrowable cnfe = reinterpret_cast<jthrowable>(
env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
WellKnownClasses::java_lang_ClassNotFoundException_init,
javaName,
cause.get()));
if (cnfe != nullptr) {
// 抛出异常
env->Throw(cnfe);
}
return nullptr;
}
if (initialize) {
// 如果class不为空 , 并且已经初始化完 , 则会再确认初始化
class_linker->EnsureInitialized(soa.Self(), c, true, true);
}
// 返回class对象的引用
return soa.AddLocalReference<jclass>(c.Get());
}
- 在
ClassLinker.FindClass
中开始寻找Class指针
- 通过
LookupClass
找Class指针 - 如果Class加载失败 , 并且传入的classloader为空 , 则通过boot_class_path加载
- 如果从boot_class_path找到了Class , 则会通过
DefineClass
加载class并且返回 - 开始从BootClassloader中寻找class
- 如果没找到 , 则判断当前线程是否允许回调Java层函数 , 失败则抛出异常
- 如果允许回调Java层函数 , 则开始校验描述符规则
- 通过描述符规则校验后 , 调用classLoader.loadClass返回class指针
- 找到class后 , 会将ClassLoader的ClassTable更新
- 最后返回class指针
mirror::Class* ClassLinker::FindClass(Thread* self,
const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
// 计算描述符的hash值 , 即Lcom/test/test的hash值
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// 在ClassLinker的ClassTable中找Class对象
ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// 如果Class还没有加载的话 , 并且传入的ClassLoader没有创建的话
if (descriptor[0] != '[' && class_loader == nullptr) {
// 开在boot class path中寻找class对象
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
// 找到的话 , 则通过DefineClass返回 class对象
return DefineClass(self,descriptor, hash, ScopedNullHandle<mirror::ClassLoader>(),
*pair.first, *pair.second);
} else {
// 如果BootClassPath没有找到的话 , 就抛除NoClassDefFoundError
ObjPtr<mirror::Throwable> pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return nullptr;
}
}
// 返回结果对应的Class的指针
ObjPtr<mirror::Class> result_ptr;
bool descriptor_equals;
// 如果描述符是个数组的话
if (descriptor[0] == '[') {
// 通过classLoader创建Array Class
result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
descriptor_equals = true;
} else {
ScopedObjectAccessUnchecked soa(self);
// 开始在BaseDexClassLoader中查找Class
bool known_hierarchy =
FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
if (result_ptr != nullptr) {
// 如果在BaseDexClassLoader中找到Class
descriptor_equals = true;
} else {
// 如果当前线程不允许从JNI回调到JAVA层到话
if (!self->CanCallIntoJava()) {
// 抛出NolassDefFoundError
ObjPtr<mirror::Throwable> pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return nullptr;
}
// 获取描述符的长度
size_t descriptor_length = strlen(descriptor);
// 开始校验描述符是否正确
if (UNLIKELY(descriptor[0] != 'L') ||
UNLIKELY(descriptor[descriptor_length - 1] != ';') ||
UNLIKELY(memchr(descriptor + 1, '.', descriptor_length - 2) != nullptr)) {
// 如果校验错误 , 则返回NolassDefFoundError错误
ThrowNoClassDefFoundError("Invalid descriptor: %s.", descriptor);
return nullptr;
}
// 截取Class名字
std::string class_name_string(descriptor + 1, descriptor_length - 2);
// 把com/test/test替换成com.test.test包名
std::replace(class_name_string.begin(), class_name_string.end(), '/', '.');
// 获取ClassLoader在本线程内的引用
ScopedLocalRef<jobject> class_loader_object(
soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
// 获取class_name
ScopedLocalRef<jobject> class_name_object(
soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
// 调用JNIEnv->CallObjectMethod方法 , 也就是调用ClassLoader.loadClass(className)
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (result.get() == nullptr && !self->IsExceptionPending()) {
// 如果ClassLoader.loadClass失败的话 , 则打印日志并且返回
ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
}
// 如果result.get不为空 , 则获取到class的指针
result_ptr = soa.Decode<mirror::Class>(result.get());
// 再检查一遍Descriptor
descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
}
}
// 如果还有Pending的异常没处理的话 , 则会再重新找一遍class
if (self->IsExceptionPending()) {
// 有可能其他线程加载完成了这个class , 所以需要再查找一遍
result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
// 如果没有问题的话 , 则会清理异常
self->ClearException();
// 重新检查完没有异常的话 , 就会返回class指针
return EnsureResolved(self, descriptor, result_ptr);
}
return nullptr;
}
// 开始插入class table中
ObjPtr<mirror::Class> old;
{
// 加锁
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
// 将classLoader中的classTable插入
ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
// 在class_table中查找descriptor
old = class_table->Lookup(descriptor, hash);
// 如果old为空 , 代表原来的class_Table中缺失没有
if (old == nullptr) {
// 将class的指针赋给old
old = result_ptr;
if (descriptor_equals) {
// 如果class与descriptor匹配上了 , 则将class的指针与对应的hash值插入class_Table
class_table->InsertWithHash(result_ptr.Ptr(), hash);
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
} // else throw below, after releasing the lock.
}
}
// 返回class的指针 , 即地址
return result_ptr.Ptr();
}
- 在
ClassLinker.LookupClass
中会从 :
- ClassLoader中获取
ClassTable
- 接着从
ClassLoader
中查找descriptor
对应的ClassTable对象 - 再从
ClassTable
中获取descriotor
对应的Class的指针
mirror::Class* ClassLinker::LookupClass(Thread* self,
const char* descriptor,
size_t hash,
ObjPtr<mirror::ClassLoader> class_loader) {
// 锁住ClassLoader所在的线程
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// 从ClassLoader中获取ClassTable
ClassTable* const class_table = ClassTableForClassLoader(class_loader);
if (class_table != nullptr) {
// 从ClassTable中获取descriptor对应的Class指针
ObjPtr<mirror::Class> result = class_table->Lookup(descriptor, hash);
if (result != nullptr) {
// 如果找到则返回class对应的指针
return result.Ptr();
}
}
return nullptr;
}
Class.getDeclaredMethods
在Class.getDeclaredMethods
调用后 , 同样会调用到JNI层
- 通过
jobject
找到Class对象的地址 - 根据
klass->GetDeclaredMethods
会先获取到method_
的基址 - 根据
8bit
进行分割 , 通过这些指针构建成Method
对象
static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis,
jboolean publicOnly) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
// 解析javaThis的class地址
Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t num_methods = 0;
// 根据Method地址所占用的长度开始遍历Method对象
for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
auto modifiers = m.GetAccessFlags();
// 计算Method数量
if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) &&
(modifiers & kAccConstructor) == 0) {
++num_methods;
}
}
// 初始化Method的ArrayClass数组
auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc(
soa.Self(), mirror::Method::ArrayClass(), num_methods));
if (ret == nullptr) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
num_methods = 0;
// 又开始遍历一遍
for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
auto modifiers = m.GetAccessFlags();
// 满足下面条件
if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) &&
(modifiers & kAccConstructor) == 0) {
// 通过函数指针(即m)创建ARTMethod对象
auto* method =
mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m);
if (method == nullptr) {
soa.Self()->AssertPendingException();
return nullptr;
}
// 添加method
ret->SetWithoutChecks<false>(num_methods++, method);
}
}
// 返回Mthod数组
return soa.AddLocalReference<jobjectArray>(ret.Get());
}
根据Class对象中methods_
属性的值 , 代表Method的基址
inline LengthPrefixedArray<ArtMethod>* Class::GetMethodsPtr() {
return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
static_cast<uintptr_t>(GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_))));
}
FAQ
- 多线程同时加载类是否会有问题
回答 : 不会有问题 , 在类加载的时候 , 都有Mutex
进行加锁