Java反射原理 您所在的位置:网站首页 反射原理java Java反射原理

Java反射原理

2024-06-18 14:53| 来源: 网络整理| 查看: 265

简述

对于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的ClassTableClass.getDeclaredMethods 通过Class对象找到method_的值 , 即为方法区的地址通过8bit的大小来分割Method的地址Class.forName在Java层通过Class.forName来查找对应的Class如果传入的classLoader为空 , 则会通过VMStack.getCallingClassLoader获取ClassLoader代码语言:javascript复制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指针代码语言:javascript复制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 hs(soa.Self()); // 获取javaLoader的指针 Handle class_loader( hs.NewHandle(soa.Decode(javaLoader))); // 从Rumtime.Current中获取ClassLinker对象 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // 通过ClassLinker.findClass找到Class对象 , 并且创建相关Handle Handle c( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader))); // 如果从class_table与ClassLoader中加载/获取失败的话 if (c == nullptr) { ScopedLocalRef cause(env, env->ExceptionOccurred()); env->ExceptionClear(); jthrowable cnfe = reinterpret_cast( 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(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指针代码语言:javascript复制mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, Handle class_loader) { // 计算描述符的hash值 , 即Lcom/test/test的hash值 const size_t hash = ComputeModifiedUtf8Hash(descriptor); // 在ClassLinker的ClassTable中找Class对象 ObjPtr 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(), *pair.first, *pair.second); } else { // 如果BootClassPath没有找到的话 , 就抛除NoClassDefFoundError ObjPtr pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); self->SetException(pre_allocated); return nullptr; } } // 返回结果对应的Class的指针 ObjPtr 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 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 class_loader_object( soa.Env(), soa.AddLocalReference(class_loader.Get())); ScopedLocalRef result(soa.Env(), nullptr); { ScopedThreadStateChange tsc(self, kNative); // 获取class_name ScopedLocalRef 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(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 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的指针代码语言:javascript复制mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, ObjPtr 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 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对象代码语言:javascript复制static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, jboolean publicOnly) { ScopedFastNativeObjectAccess soa(env); StackHandleScope hs(soa.Self()); // 解析javaThis的class地址 Handle 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::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(soa.Self(), &m); if (method == nullptr) { soa.Self()->AssertPendingException(); return nullptr; } // 添加method ret->SetWithoutChecks(num_methods++, method); } } // 返回Mthod数组 return soa.AddLocalReference(ret.Get()); }

根据Class对象中methods_属性的值 , 代表Method的基址

代码语言:javascript复制inline LengthPrefixedArray* Class::GetMethodsPtr() { return reinterpret_cast( static_cast(GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_)))); }FAQ多线程同时加载类是否会有问题 回答 : 不会有问题 , 在类加载的时候 , 都有Mutex进行加锁参考资料

JNI 提示



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有