当前位置: 首页 > news >正文

ART虚拟机 | 类加载详解

平台:Android14(开源)

目录

一、什么是类加载?

二、类加载流程

​三、类加载源码分析

四、类对象内存布局与核心数据结构

五、可优化方向


一、什么是类加载?


应用冷启动过程的各个阶段(如bindApplication阶段、Activity启动阶段等)通过类加载器将所需的类加载到内存中,并在运行时创建类的实例,提供给程序使用。

  • ClassLoader类加载器分类

BootClassLoader:加载系统框架类。
PathClassLoader:加载应用自己的类。

  • 类加载时机

被加载类的类型:被加载类可分为两种,分别是系统框架类和应用自己的类。
应用冷启动的bindApplication阶段加载系统框架类、在activity启动阶段加载应用自己的类。


二、类加载流程


类加载主要包括三个阶段:加载、链接、初始化。应用冷启动类加载只会对系统框架类和应用自己的类做加载,加载完毕后,会将类定义加入到classTable中,以便后续再需要加载这个类中,直接从classTable中查找。



三、类加载源码分析


dex文件的加载,即将dex文件加载到内存中并构建DexFile对象(https://blog.csdn.net/youthcowboy/article/details/150352030?spm=1001.2014.3001.5502)和类的加载(从已加载的DexFile对象中查找并定义某个类),两者是通过ClassLoader联系在一起。
ClassLoader在初始化时会加载指定的DexFile对象,这些DexFile对象被封装在DexPathList中的Element数组里。当需要加载一个类时,ClassLoader会遍历这些Element,每个Element对应一个已加载的dex文件,然后尝试从这些dex文件中加载类。
类加载分为两类:1、加载系统框架类,在bindApplication阶段和Activity启动阶段都会去做加载 2、加载应用自己的类,在Activity启动阶段会去做加载

  • bindApplication阶段加载依赖类(系统框架类)

在应用冷启动的bindApplication阶段会去加载系统框架类。如下:
遍历dexElements数组,依次尝试从每个DexFile中加载类(系统框架类)。

// frameworks/base/core/java/android/app/ActivityThread.java
private void attach(boolean system, long startSeq) {...// 调用AMS的attachApplication binder接口final IActivityManager mgr = ActivityManager.getService();mgr.attachApplication(mAppThread, startSeq);...
}
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javapublic final void attachApplication(IApplicationThread thread, long startSeq) {...attachApplicationLocked(thread, callingPid, callingUid, startSeq);...
}
private void attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {...// 回调ActivityThread类中的bindApplication接口,其中一个操作任务是做OpenDexFilesFromOatthread.bindApplication(...);...
}
// frameworks/base/core/java/android/app/ActivityThread.java
public final void bindApplication(...) {...// 发送messagesendMessage(H.BIND_APPLICATION, data);...
}
public void handleMessage(Message msg) {switch (msg.what) {// 处理binder消息case BIND_APPLICATION:AppBindData data = (AppBindData)msg.obj;handleBindApplication(data);break;...
}
private void handleBindApplication(AppBindData data) {...// 会执行dex文件加载任务、类加载任务data.info.makeApplicationInner(data.restrictedBackupMode, null);...        
}
// frameworks/base/core/java/android/app/LoadedApk.java      
private Application makeApplicationInner(boolean forceDefaultAppClass,Instrumentation instrumentation, boolean allowDuplicateInstances) {...// 1、执行dex文件加载任务final java.lang.ClassLoader cl = getClassLoader();...// 2、使用ClassLoader从dex文件中加载系统框架类mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);...
}// frameworks/base/core/java/android/app/Instrumentation.java 
public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException,ClassNotFoundException {Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);app.attach(context);return app;
}// frameworks/base/core/java/android/app/AppComponentFactory.java 
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,@NonNull String className)throws InstantiationException, IllegalAccessException, ClassNotFoundException {return (Application) cl.loadClass(className).newInstance();
}// libcore/ojluni/src/main/java/java/lang/ClassLoader.java
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {// 第一步:检查类是否已被加载(缓存查找)Class<?> c = findLoadedClass(name);if (c == null) {try {// 第二步:双亲委派 - 先让父加载器(BaseDexClassLoader)尝试加载if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// 父加载器找不到,继续往下执行}if (c == null) {// 第三步:父加载器(BaseDexClassLoader)找不到,调用自己的findClass方法c = findClass(name);}}return c;
}// libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {// First, check whether the class is present in our shared libraries.if (sharedLibraryLoaders != null) {for (ClassLoader loader : sharedLibraryLoaders) {try {return loader.loadClass(name);} catch (ClassNotFoundException ignored) {}}}// Check whether the class in question is present in the dexPath that// this classloader operates on.List<Throwable> suppressedExceptions = new ArrayList<Throwable>();Class c = pathList.findClass(name, suppressedExceptions);if (c != null) {return c;}...
}// libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public Class<?> findClass(String name, List<Throwable> suppressed) {// 一个element对应一个dex文件目录for (Element element : dexElements) {Class<?> clazz = element.findClass(name, definingContext, suppressed);if (clazz != null) {return clazz;}}if (dexElementsSuppressedExceptions != null) {suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));}return null;
}
// 内部类Element
public Class<?> findClass(String name, ClassLoader definingContext,List<Throwable> suppressed) {return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed): null;
}// libcore/dalvik/src/main/java/dalvik/system/DexFile.java
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,DexFile dexFile, List<Throwable> suppressed) {Class result = null;try {// native层类加载实现result = defineClassNative(name, loader, cookie, dexFile);} catch (NoClassDefFoundError e) {if (suppressed != null) {suppressed.add(e);}} catch (ClassNotFoundException e) {if (suppressed != null) {suppressed.add(e);}}return result;
}

native层类加载实现:

// art/runtime/native/dalvik_system_DexFile.cc 
static jclass DexFile_defineClassNative(JNIEnv* env,jclass,jstring javaName,jobject javaLoader,jobject cookie,jobject dexFile) {std::vector<const DexFile*> dex_files;const OatFile* oat_file;if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {VLOG(class_linker) << "Failed to find dex_file";DCHECK(env->ExceptionCheck());return nullptr;}...for (auto& dex_file : dex_files) {const dex::ClassDef* dex_class_def =OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);if (dex_class_def != nullptr) {ScopedObjectAccess soa(env);ClassLinker* class_linker = Runtime::Current()->GetClassLinker();StackHandleScope<1> hs(soa.Self());Handle<mirror::ClassLoader> class_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));ObjPtr<mirror::DexCache> dex_cache =class_linker->RegisterDexFile(*dex_file, class_loader.Get());if (dex_cache == nullptr) {// OOME or InternalError (dexFile already registered with a different class loader).soa.Self()->AssertPendingException();return nullptr;}ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),descriptor.c_str(),hash,class_loader,*dex_file,*dex_class_def);...return nullptr;
}// art/runtime/class_linker.cc
ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,const char* descriptor,size_t hash,Handle<mirror::ClassLoader> class_loader,const DexFile& dex_file,const dex::ClassDef& dex_class_def) {// 1. 读取类的Dex信息ClassDataIterator it(dex_file, class_def);// 2. 创建Class对象mirror::Class* new_class = mirror::Class::Alloc</kMovable>(self, GetClassRoot<mirror::Class>());// 3. 设置类的基本信息klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));klass->SetDexTypeIndex(dex_class_def.class_idx_);// 4. 加载类的超类和接口LoadSuperAndInterfaces(klass, *new_dex_file);// 5. 将类插入到类表中ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);// 6. 加载类的字段和方法LoadClass(self, *new_dex_file, *new_class_def, klass);return new_class;
}
  • LoadClass  ---加载类的字段和方法
void ClassLinker::LoadClass(Thread* self,const DexFile& dex_file,const dex::ClassDef& dex_class_def,Handle<mirror::Class> klass) {// 1. 创建 Dex 文件访问器,用于读取类数据ClassAccessor accessor(dex_file,dex_class_def,/* parse_hiddenapi_class_data= */ klass->IsBootStrapClassLoaded());...Runtime* const runtime = Runtime::Current();{ScopedAssertNoThreadSuspension nts(__FUNCTION__);// 2. 加载字段,为静态字段(sfields)和实例字段(ifields)分配内存LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self,allocator,accessor.NumStaticFields());LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,allocator,accessor.NumInstanceFields());...// 3. 加载方法,为方法分配内存,区分直接方法(构造函数、静态方法等)和虚方法bool has_oat_class = false;const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler())? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class): OatFile::OatClass::Invalid();const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;klass->SetMethodsPtr(AllocArtMethodArray(self, allocator, accessor.NumMethods()),accessor.NumDirectMethods(),accessor.NumVirtualMethods());...uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();// 4. 遍历加载字段、加载方法并链接accessor.VisitFieldsAndMethods([&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {uint32_t field_idx = field.GetIndex();DCHECK_GE(field_idx, last_static_field_idx);  // Ordering enforced by DexFileVerifier.if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) {LoadField(field, klass, &sfields->At(num_sfields));++num_sfields;last_static_field_idx = field_idx;}}, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {uint32_t field_idx = field.GetIndex();DCHECK_GE(field_idx, last_instance_field_idx);  // Ordering enforced by DexFileVerifier.if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) {LoadField(field, klass, &ifields->At(num_ifields));++num_ifields;last_instance_field_idx = field_idx;}}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,image_pointer_size_);LoadMethod(dex_file, method, klass.Get(), art_method);LinkCode(this, art_method, oat_class_ptr, class_def_method_index);...}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {ArtMethod* art_method = klass->GetVirtualMethodUnchecked(...LoadMethod(dex_file, method, klass.Get(), art_method);LinkCode(this, art_method, oat_class_ptr, class_def_method_index);++class_def_method_index;});...
}
  • LinkCode  ---链接

什么是链接?就是将孤立的各个部分串接起来,并固定最终的位置的过程。
LinkCode决定方法执行时使用解释器、JIT 还是 AOT 编译代码。

static void LinkCode(ClassLinker* class_linker,ArtMethod* method,const OatFile::OatClass* oat_class,uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {ScopedAssertNoThreadSuspension sants(__FUNCTION__);// 只在运行时环境执行链接,不在编译时执行Runtime* const runtime = Runtime::Current();if (runtime->IsAotCompiler()) {return;}...// 从 OAT 文件中获取预编译的本地代码const void* quick_code = nullptr;if (oat_class != nullptr) {// Every kind of method should at least get an invoke stub from the oat_method.// non-abstract methods also get their code pointers.const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);quick_code = oat_method.GetQuickCode();}// 设置方法的快速编译代码入口点runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code);// 为 JNI 方法设置动态链接桩(在运行时动态查找并调用jni方法)if (method->IsNative()) {method->SetEntryPointFromJni(method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());}
}
  • Activity启动阶段加载依赖类(应用自己的类 + 系统框架类)

在应用冷启动的performLaunchActivity阶段(bindApplication之后)会去加载应用自己的类和系统框架类。

// frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);...
}// 后续代码流程和bindApplication阶段的类加载一致
  • 代码时序

四、类对象内存布局与核心数据结构

  • 类对象内存布局

IMTable(Interface Method Table)和VTable(Virtual Table):在调用接口方法和虚方法时能够保证多态的正确执行。
- 核心数据结构
  - struct ClassDef
  dex::ClassDef 是 Dex文件格式中定义的一个结构,记录了一个类的定义。在 Android 的 Dex 文件中,每个类都有一个对应的 ClassDef 结构,它包含了这个类的基本信息。
  类加载时,根据这个数据结构来创建对应的 mirror::Class 对象(ART中表示类的内部结构)。

struct ClassDef {uint32_t class_idx_;           // 类类型索引(指向type_ids)uint32_t access_flags_;        // 访问标志(public、final等)uint32_t superclass_idx_;      // 父类索引uint32_t interfaces_off_;      // 接口列表偏移量uint32_t source_file_idx_;     // 源文件名字符串索引uint32_t annotations_off_;     // 注解偏移量uint32_t class_data_off_;      // **类数据偏移量(最重要的字段)**uint32_t static_values_off_;   // 静态初始值偏移量
};
  •   mirror::Class

  ART 运行时中 Java 类的内部表示,它桥接了 Java 类的抽象描述(如在 Dex 文件中的定义)和运行时的具体实例对象。

// art/runtime/mirror/class.h
namespace mirror {class ClassExt;
class ClassLoader;
class Constructor;
class DexCache;
class Field;
class IfTable;
class Method;
template <typename T> struct PACKED(8) DexCachePair;// C++ mirror of java.lang.Class
class MANAGED Class final : public Object {public:MIRROR_CLASS("Ljava/lang/Class;");...
}
};

五、可优化方向


可以考虑将应用冷启动的Activity启动阶段的类(应用自己的类 + 系统框架类)加载操作提前放到bindApplication阶段去执行,缩短Activity启动阶段的耗时,优化应用冷启动整体速度。

http://www.dtcms.com/a/578248.html

相关文章:

  • 做面包网站wordpress外贸发布接口
  • 【AI应用探索】-7- LLaMA-Factory微调模型
  • AWS Elastic Beanstalk 实现 Java 应用高可用部署指南
  • 监理网站河南网站seo营销多少费用
  • 网站的注册做网站需要提供什么条件
  • 密码学系统的核心防护:FUZZ测试(模糊测试)技术原理与行业实践
  • ubuntu20.0.4源码安装Colmap流程
  • 做网站卖东西送上门网站title的写法
  • Windows 系统安装 Composer 详细教程
  • wordpress 切换域名网站代码优化有哪些
  • 中小工厂erp管理系统济南网站建设seo优化
  • 国产ASP4644I6B降压稳压器在工业仪表中的应用实践与分析
  • TRO侵权预警|Theodoros爆款食物插画发起版权维权
  • 4.1 Boost库工具类noncopyable的使用
  • ubuntu连接airpods
  • wordpress评分点评成都网站搭建优化推广
  • 保山手机网站建设影视公司网站建设
  • Poll 服务器实战教学:从 Select 迁移到更高效的多路复用
  • 代码管理——VS Code|Git
  • SkyWalking运维之路(Java探针接入)
  • 四川省建设厅注册中心网站wordpress主页加音乐
  • 广州企业网站模板建站专业做阿里巴巴网站的公司
  • 网站的ftp账号和密码谷歌外贸建站
  • 全球生物识别加密U盘市场:安全需求驱动增长,技术迭代重塑格局
  • 从 ChatGPT 到 OpenEvidence:AI 医疗的正确打开方式
  • 彩票网站自己可以做吗网站广告设计
  • 面试后查缺补漏--cmake,makefiles,g++,gcc(AI写)
  • C++入门(三) (算法竞赛)
  • 遵义市网站建设网络营销产品推广
  • Docker-仓库-镜像制作