美文网首页
Android 2.3中的LinearAlloc

Android 2.3中的LinearAlloc

作者: None_Ling | 来源:发表于2020-12-01 15:57 被阅读0次

    原因

    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
    2. 在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分析

    相关文章

      网友评论

          本文标题:Android 2.3中的LinearAlloc

          本文链接:https://www.haomeiwen.com/subject/bypdwktx.html