前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android 2.3中的LinearAlloc

Android 2.3中的LinearAlloc

作者头像
None_Ling
发布于 2020-12-03 06:42:29
发布于 2020-12-03 06:42:29
72100
代码可运行
举报
文章被收录于专栏:Android相关Android相关
运行总次数: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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
区块链NFT系统的上线流程
区块链NFT系统的上线流程是一个系统性的工程,涵盖了从概念规划到发布运营的多个阶段。以下是一个详细的上线流程,其中包含了关键的步骤和注意事项。
数字孪生开发者
2025/06/10
800
区块链NFT系统的上线流程
195万NFT用户创造超170亿美元市场,你是其中一员吗?
今天你铸造 NFT 了吗?截至3月9日,全球持有 NFT 的用户已经上涨到195万人,创造了超170亿美元的市场。
本体Ontology
2022/03/29
2730
195万NFT用户创造超170亿美元市场,你是其中一员吗?
数字藏品(NFT)项目的上线流程
数字藏品(NFT)项目的上线流程是一个涉及多个环节的复杂过程,需要精心策划和执行。以下是一个详细的步骤指南,涵盖了从准备阶段到上线后的维护。
数字孪生开发者
2025/03/28
1350
数字藏品(NFT)项目的上线流程
音乐NFT项目的技术开发
音乐 NFT 项目的技术开发是一个涉及多个环节的复杂过程,需要结合区块链技术、数字内容管理、用户界面设计等多个领域的知识。以下是音乐 NFT 项目的技术开发流程和关键步骤。
数字孪生开发者
2025/04/09
1350
音乐NFT项目的技术开发
TME入局背后 音乐NFT的用户市场、应用价值与海外实践
今年3月,加密艺术家Beeple的NFT作品《Everydays:The First 5000 Days》在佳士得以6025万美元的拍卖价成交,让NFT概念被更多人看到。
用户7358413
2021/10/21
1K0
艺术品NFT系统的开发流程
艺术品NFT系统的开发是一个涵盖艺术创作、区块链技术、智能合约和市场运营的复杂过程。以下是一个详细的开发流程。
数字孪生开发者
2025/06/03
1200
艺术品NFT系统的开发流程
音乐NFT智能合约开发
音乐NFT的智能合约开发是音乐NFT项目的核心技术环节,它决定了NFT的铸造、交易、版权管理等功能是否能够安全、高效地运行。以下是音乐NFT智能合约开发的详细流程和关键要点。
数字孪生开发
2025/03/07
1050
音乐NFT智能合约开发
艺术品NFT的上线流程
艺术品NFT的上线流程不仅仅是技术上的部署,更是一个全面的市场推广和社区建设过程。一个成功的上线需要精心的策划和执行。以下是一个详细的艺术品NFT上线流程。
数字孪生开发者
2025/06/03
1120
艺术品NFT的上线流程
数字藏品(NFT)系统的上线运营
数字藏品(NFT)系统的上线运营是一个复杂的过程,涉及技术开发、市场策略、法律合规和社区建设等多个方面。
数字孪生开发者
2025/06/10
1670
数字藏品(NFT)系统的上线运营
元宇宙音乐,无界的狂欢party!
最近一段时间,“买菜难”成为上海最牵动人心的公共话题。但富有娱乐精神,善于苦中作乐的上海人,也在居家封控中制造各种“云娱乐”。
科技旋涡
2022/04/28
6030
元宇宙音乐,无界的狂欢party!
数字藏品NFT的开发框架
数字藏品NFT(非同质化代币)的开发框架涉及区块链技术、智能合约、存储解决方案、用户交互等多个方面。以下是一个完整的数字藏品NFT开发框架,涵盖从技术选型到部署上线的关键步骤。
数字孪生开发者
2025/01/26
2520
数字藏品NFT的开发框架
音乐NFT项目的技术架构
一个音乐 NFT 项目的技术架构通常包含多个关键组件,这些组件协同工作以实现音乐 NFT 的创建、交易、管理和可能的播放等功能。以下是一个典型的技术架构概览。
数字孪生开发者
2025/04/08
1330
音乐NFT项目的技术架构
嚯!终于知道什么是 NFT 了,还有它的运行机制!
NFT是一种令牌,我们可以用它来表示唯一物品的所有权。它们让我们把艺术品、收藏品、甚至房地产等东西当作令牌。它们一次只能有一个官方所有者,并且受到以太坊区块链的保护——没有人可以修改所有权记录或复制/粘贴一个新的NFT。
前端修罗场
2022/07/29
8650
接受比特币付款、在以太坊上发歌,当音乐遇上区块链
2017年,加密货币Monero(门罗币)曾宣布,有45名音乐人和5家在线商店在圣诞假期接受加密货币支付,并向那些使用加密货币支付的人提供折扣。
区块链大本营
2019/05/17
8000
NFT Oasis指南
多年来,所有权的概念不仅包括有形资产,还包括数字资产。然而,证明数字所有权和真实性有其挑战,许多允许您访问数字内容的网络平台根本不允许拥有和控制这些资产。这只是 NFT 世界的一部分。
公众号---人生代码
2021/08/26
5560
NFT Oasis指南
音乐NFT软件系统的上线流程
音乐NFT软件系统的上线流程涉及多个阶段,从需求分析到最终部署和推广,每个阶段都至关重要。以下是详细的上线流程。
数字孪生开发者
2025/03/14
470
音乐NFT软件系统的上线流程
音乐NFT开发中的主要技术难点
音乐NFT(非同质化代币)的开发涉及区块链技术、数字版权管理、音频处理、用户体验设计等多个领域,存在一些技术难点需要解决。以下是音乐NFT开发中的主要技术难点及其解决方案。
数字孪生开发者
2025/01/26
880
音乐NFT开发中的主要技术难点
音乐NFT系统的技术难点
音乐 NFT 系统,即将音乐作品或相关权益以 NFT(非同质化代币)的形式在区块链上进行发行、交易和管理的系统,其开发涉及多个技术和非技术层面的难点。以下我将详细阐述这些难点。
数字孪生开发
2024/12/23
1770
音乐NFT系统的技术难点
Web3赋能新商业模式
编者按:互联网行业的迅猛发展带动了音视频技术的进步,一些新鲜术语也随之进入音视频赛道。一股Web3热潮正席卷而来,相比元宇宙、NFT、虚拟人这些更为火热的概念,Web3到底是什么意思?从概念到落地,海外Web3音视频发展趋势如何?为此,我们很荣幸地邀请到了Reddio 创始人&CEO Neil HAN,分享了围绕Web3技术的最新进展、给音乐行业带来的变革及可能、元宇宙内的音视频、直播新玩法、Web3未来展望等五部分精彩内容,期待一起走进Web3时代。
LiveVideoStack
2023/01/10
5560
Web3赋能新商业模式
收藏品NFT的开发流程
通过以上流程,可以系统性地开发和发布收藏品NFT。根据项目的规模和需求,可以选择自行开发或与第三方开发团队合作完成。
数字孪生开发者
2024/12/09
1660
收藏品NFT的开发流程
相关推荐
区块链NFT系统的上线流程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档