首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 2.3中的LinearAlloc

Android 2.3中的LinearAlloc

作者头像
None_Ling
发布2020-12-03 14:42:29
6400
发布2020-12-03 14:42:29
举报
文章被收录于专栏:Android相关Android相关

原因

2.3版本Apk安装时 , 会进行Dexopt , 如果单个Dex中的class过大/method过多 , 就会导致LinearAlloc为Class/Method的内存分配不足 , 从而让Dexopt进程挂掉.

而如果存在Multidex的话 , Multidex会为多个Dex执行多次Dexopt操作 , 所以 , 如果也存在的话 , 也会导致LinearAlloc超限.

同时 , 在运行时加载Class文件时 , 也会使用LinearAlloc为Interface、Method分配内存 , 如果超出5M限制 , 就会报LinearAlloc exceeded capacity异常 , 会导致DVM虚拟机异常.

在加载类时 , 会使用LinearAlloc为Class的以下属性分配内存空间 :

  • Interfaces : 大小 : count * (sizeof(ClassObject*))
  • InstantceFields : 大小 : count * sizeof(InstField)
  • Direct Methods : 大小 : count * sizeof(Method)
  • Virtual Methods : 大小 : count * sizeof(Method)
  • vtable : 大小 : sizeof(Method*) *maxCount
  • iftable : 大小 : sizeof(InterfaceEntry) * ifCount
01-24 11:14:54.884: I/dalvikvm(12382): DexOpt: not resolving ambiguous class 'Lorg/apache/commons/codec/DecoderException;'
01-24 11:14:55.935: E/dalvikvm(12382): LinearAlloc exceeded capacity (5242880), last=80
01-24 11:14:55.935: E/dalvikvm(12382): VM aborting
01-24 11:14:56.265: I/DEBUG(1257): debuggerd: 2013-01-24 11:14:56
01-24 11:14:56.265: I/DEBUG(1257): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-24 11:14:56.265: I/DEBUG(1257): Build fingerprint: 'htc_asia_wwe/htc_ace/ace:2.3.5/GRJ90/228204.4:user/release-keys'
...
01-24 11:14:56.306: W/installd(1263): DexInv: --- END '/data/app/com.realcloud.loochadroid.campuscloud-1.apk' --- status=0x000b, process failed
01-24 11:14:56.306: E/installd(1263): dexopt failed on '/data/dalvik-cache/data@app@com.realcloud.loochadroid.campuscloud-1.apk@classes.dex' res = 11
01-24 11:14:56.306: W/PackageManager(1379): Package couldn't be installed in /data/app/com.realcloud.loochadroid.campuscloud-1.apk
01-24 11:14:56.466: D/dalvikvm(1379): GC_EXPLICIT freed 2597K, 40% free 11337K/18759K, external 1573K/2080K, paused 146ms

解决方案

  1. 在打MultiDex的时候 , 添加dx的参数--ser-max-idx-number=48000 , 让每个Dex的最大方法数最大为48000 , 避免出现LinearAlloc
dexOptions {
            javaMaxHeapSize "2g"
            additionalParameters += '--multi-dex'
            // 设置Dex的最大方法数
            additionalParameters += '--set-max-idx-number=62000'
        }
  1. 配置Proguard , 优化代码 , 减少方法和类数量.
  2. Dex过多会导致2.x的版本 , 可能会出现ANR的问题 , 可以通过多进程Dexopt来处理该问题.

流程

  1. 以2.3版本为例LinearAlloc最大内存为 : 5M

image.png

在调用dvmLinearAllocCreate函数中 , 会通过ashmem_create_region创建一片5M大小的内存空间

image.png

  1. class.cc中 , 会调用dvmClassStartup函数创建LinearAllocHdr对象 , 并且赋值给gDvm.pBootLoaderAlloc用于后续的dex加载

image.png

  1. LinearAlloc.cc中调用dvmLinearAlloc为classLoader分配大小

image.png

  1. 在ClassLoader将Odex文件加载入内存后, 校验Odex的CRC32、签名、Magic等 , 并且构建DexFile结构用于标识Method、Class等方法入口.

当使用到Class时 , 将会调用findClass来加载类文件 , 同时使用LinearAlloc为interface、Method分配内存.

ClassLoder defineClass借图

  1. findClassNoInit函数比较简单 , 主要有以下步骤 :
  • 从HashTable中查找descrioptor对应的class类
  • 找到Class对应的DexFile对象
  • 从DexFile中加载Class对象
  • 将Class对象添加到HashTable中
  • 开始Resolve Class
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
    DvmDex* pDvmDex)
{
     ... 
     // 先从HashTable中查找类
    clazz = dvmLookupClass(descriptor, loader, true);
    if (clazz == NULL) {
        ......
        // 如果没找到对应Dex , 则从BootPath中查找
        if (pDvmDex == NULL) {
            assert(loader == NULL);     /* shouldn't be here otherwise */
            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
        } else {
           // 从DexFile中找到Class
           pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
        }

        ......

       // 如果找到了Class , 则从Dex文件中加载该类
       clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
         ...
        //  将Class对象添加到HashTable中
       if (!dvmAddClassToHash(clazz)) {
            // 如果添加失败 , 则开始查找class对象
            clazz = dvmLookupClass(descriptor, loader, true);
            assert(clazz != NULL);
            goto got_class;
        }
         // 开始Resolve Class , 也就是Link Class
       if (!dvmLinkClass(clazz)) {
            ......
        }    
    ...
    return clazz;
}
  1. dexDefineClass
const DexClassDef* dexFindClass(const DexFile* pDexFile,
    const char* descriptor)
{
    const DexClassLookup* pLookup = pDexFile->pClassLookup;
    u4 hash;
    int idx, mask;
    hash = classDescriptorHash(descriptor);
    mask = pLookup->numEntries - 1;
    idx = hash & mask;
    while (true) {
        int offset;
        // 遍历DexFile table的ClassDescriotor
        offset = pLookup->table[idx].classDescriptorOffset;
       if (offset == 0)
            return NULL;
        // 如果找到了descriptor对应的hash值
        if (pLookup->table[idx].classDescriptorHash == hash) {
            const char* str;
            // 根据基址找到字符串
            str = (const char*) (pDexFile->baseAddr + offset);
            if (strcmp(str, descriptor) == 0) {  
               // 返回DexClasDef对象 , 对应Class的信息
               return (const DexClassDef*)
                    (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
            }
        }
        idx = (idx + 1) & mask;
    }
}
  1. loadClassFromDex中 , 会调用loadClassFromDex0加载类 , 会通过LinearAlloc分配
  • Interfaces
  • InstantceFields
  • Direct Methods
  • Virtual Methods
static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
    const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
    const u1* pEncodedData, Object* classLoader)
{
      // 返回的Class对象
     ClassObject* newClass = NULL;
     ... 
     // 初始化Class对象
     if (classLoader == NULL &&
        strcmp(descriptor, "Ljava/lang/Class;") == 0) {
        assert(gDvm.classJavaLangClass != NULL);
        newClass = gDvm.classJavaLangClass;
    } else {
        size_t size = classObjectSize(pHeader->staticFieldsSize);
        newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
    }
     // 填充super
     newClass->super = (ClassObject*) pClassDef->superclassIdx;
    ...
    const DexTypeList* pInterfacesList;
    // 得到Interface的列表
   pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
    if (pInterfacesList != NULL) {
        newClass->interfaceCount = pInterfacesList->size;
        // 使用LinearAlloc分配interface列表
        newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
                newClass->interfaceCount * sizeof(ClassObject*));

        for (i = 0; i < newClass->interfaceCount; i++) {
            const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
            newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
        }
        // 通过mprotect设置ReadOnly
        dvmLinearReadOnly(classLoader, newClass->interfaces);
    }  
    // 加载static属性列表
   if (pHeader->staticFieldsSize != 0) {
        /* static fields stay on system heap; field data isn't "write once" */
        int count = (int) pHeader->staticFieldsSize;
        u4 lastIndex = 0;
        DexField field;

        newClass->sfieldCount = count;
        for (i = 0; i < count; i++) {
            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
            loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
        }
    }
   // 初始化实例属性
   if (pHeader->instanceFieldsSize != 0) {
        int count = (int) pHeader->instanceFieldsSize;
        u4 lastIndex = 0;
        DexField field;

        newClass->ifieldCount = count;
        // 使用linearAlloc分配ifields
        newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
                count * sizeof(InstField));
        for (i = 0; i < count; i++) {
            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
            loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
        }
        dvmLinearReadOnly(classLoader, newClass->ifields);
    }
   // 初始化directMethod
   if (pHeader->directMethodsSize != 0) {
        int count = (int) pHeader->directMethodsSize;
        u4 lastIndex = 0;
        DexMethod method;
        newClass->directMethodCount = count;
        // 通过LinearAlloc分配directMethods内存
        newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
                count * sizeof(Method));
         ...
        dvmLinearReadOnly(classLoader, newClass->directMethods);
    }
    // 初始化虚函数
   if (pHeader->virtualMethodsSize != 0) {
        int count = (int) pHeader->virtualMethodsSize;
        u4 lastIndex = 0;
        DexMethod method;

        newClass->virtualMethodCount = count;
        // 通过LinearAlloc分配virtualMethod内存空间
        newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
                count * sizeof(Method));
        ...
        dvmLinearReadOnly(classLoader, newClass->virtualMethods);
    }  
    newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
    return newClass;
}

8.最后通过dvmLinkClass开始链接Class

bool dvmLinkClass(ClassObject* clazz)
{
    ......
    if (clazz->status == CLASS_IDX) {
         
        superclassIdx = (u4) clazz->super;
        clazz->super= NULL;
        // 修改状态为CLASS_LOADED
        clazz->status = CLASS_LOADED;

        if (superclassIdx != kDexNoIndex) {
          // 查找已经解析好的父类Class对象
           ClassObject* super = dvmResolveClass(clazz, superclassIdx, false);
           ... // 错误校验
            // 设置父类Class对象地址
            dvmSetFieldObject((Object *)clazz,
                              OFFSETOF_MEMBER(ClassObject, super),
                              (Object *)super);
        }
        // 如果类的interface大于0
       if (clazz->interfaceCount > 0) {
            // 解析interface
            dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
            for (i = 0; i < clazz->interfaceCount; i++) {
                assert(interfaceIdxArray[i] != kDexNoIndex);
                clazz->interfaces[i] =
                    dvmResolveClass(clazz, interfaceIdxArray[i], false);
                ......
            }
            dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
        }
    }
    ... 
    if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
        ......
    } else {
        if (dvmIsFinalClass(clazz->super)) {
             // 校验父类是否为final类
            goto bail;
        } else if (dvmIsInterfaceClass(clazz->super)) {
            // 校验父类为interface
            goto bail;
        } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
            // 校验父类是否允许访问
            goto bail;
        }
        ...
    }
     // 开始创建vtable
    if (dvmIsInterfaceClass(clazz)) {
         // 如果该类是interface的话 , 则不需要创建vtable
        int count = clazz->virtualMethodCount;
        if (count != (u2) count) {
            ALOGE("Too many methods (%d) in interface '%s'", count,
                 clazz->descriptor);
            goto bail;
        }
        dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
        for (i = 0; i < count; i++)
            clazz->virtualMethods[i].methodIndex = (u2) i;
        dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
    } else {
        // 开始创建vtable
        if (!createVtable(clazz)) {
            ALOGW("failed creating vtable");
            goto bail;
        }
    }
     //  创建interface table
   if (!createIftable(clazz))
        goto bail;
    ...
bail:
    if (!okay) {
        clazz->status = CLASS_ERROR;
        if (!dvmCheckException(dvmThreadSelf())) {
            dvmThrowVirtualMachineError(NULL);
        }
    }
    if (interfaceIdxArray != NULL) {
        free(interfaceIdxArray);
    }
    return okay;
}
  1. 在创建Vtable的时候 , 也会通过LinearAlloc分配VTable的内存

image.png 10.在创建interface的table的时候 , 也会通过LinearAlloc分配iftable的内存

createIftable

至此 , Class加载完成.

参考资料

Dalvik虚拟机 - 类的加载 Android类加载器 Android - Dalvik分析

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原因
  • 解决方案
  • 流程
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档