博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
移动App性能测评与优化1.5.4 dex文件优化
阅读量:5786 次
发布时间:2019-06-18

本文共 2373 字,大约阅读时间需要 7 分钟。

1.5.4 dex文件优化

为了达到优化的目的,我们需要先了解dex文件的结构。dex文件结构如表1-2所示。

表1-2 dex文件结构

区 域  描 述  内 容

Header            

索引区  String Id list 指向Data的偏移量  

      Type Id list      

      Method Prototype Id list      

      Field Id list       

      Method Id list         

      Class Definition list        

Data区 ClassData    类数据  常量及变量定义Id

                 接口Id

                 成员函数Prototype Id

                 类Annotation的偏移量

      StringData   字符串数据  类名

                 Proto字符串

                 常量字符串

      Code    函数代码     Dalvik字节码

                 函数Debuginfo的偏移量

                 函数Annotations的偏移量

      StaticValues 静态变量初始值

      Debuginfo   Debug信息 

      Annotation  Annotations

      Map list     

 

简而言之,为了节约空间,dex将原先在各个class文件中重复的信息集中放置在一起,并以索引和指针的形式支持快速访问。虚拟机能够通过索引表在Data区域中找到需要的信息。

下面我们看一个访问字符串的例子。在dex文件结构中,读取字符串需要先到StringIdList中查表,然后根据查到的地址到Data区读取内容。StringIdList的数据结构如下:

struct DexStringId {

  u4 stringDataOff;

};

现在我们模拟虚拟机读取一个字符串,来观察内存的消耗。假设有一个字符串的id = 6728,对应的地址就会是112 + 6728 = 6990。因此虚拟机首先根据string ID读取0x006990 - 0x006994的内容,此时系统会加载0x006000~0x006fff的整页内存,从Pss角度来看,会增加4KB。

虚拟机读到的内容是stringDataOff = 0x531ed4,随后虚拟机会继续从0x531ed4读取字符串内容,假设字符串长度是45字节,则虚拟机会读取0x531ed4~0x531f04的内容,但此时系统也必须加载0x531000~0x531fff的整页内存,从Pss角度来看,会再次增加4KB。

由此可见,在有些情况下,虚拟机读取data区的一个数据,就至少要消耗8KB物理内存。如果多次读取的分散在文件各处的数据,就可能会以4KB的倍数快速消耗内存。

Android SDK提供了dexdump工具来观察dex文件内容,我们以此工具来看看dex的数据内容:

dexdump classes.dex

Processing 'classes.dex'...

Opened 'classes.dex', DEX version '035'

Class #0 header:

...

Class #0            -

  Class descriptor  : 'Laaa/aaa;'

...

Class #1            -

  Class descriptor  : 'Laaa/bbb;'

...

Class #2            -

  Class descriptor  : 'Lbbb/ccc;'

...

根据对dex数据的观察,我们发现dex文件中数据基本是按类名的字母顺序进行排列的,这样同样包名的类会排在一起。但在实际程序执行中,同一个package下的类并不会全部一起调用,而是和很多其他package下的类进行交互,但mmap加载了整个页面,可能会有很多无用数据。为了减少这样的情况,我们在生成文件时要尽量将使用到的数据内容排布在一起。在APK的编译流程中,Proguard混淆工具正好是能够对类名进行修改的,可以根据程序运行的逻辑,将那些会互相调用的类改为同一个package名,这样就可以使它们的数据排布在一起。

以上表数据为例,Class的排列顺序是aaa/aaa、aaa/bbb、bbb/ccc。假设我们的应用运行逻辑是aaa/aaa、bbb/ccc,而aaa/bbb在某些特殊时候才能用到。但在当前的排列情况下,加载了aaa/aaa和bbb/ccc就必然要加载aaa/bbb。我们可以用Proguard等工具来控制类名,将aaa/bbb等不常用的类放在后面,则aaa/bbb平时就不会加载。如下所示:

dexdump classes.dex

Processing 'classes.dex'...

Opened 'classes.dex', DEX version '035'

Class #0 header:

...

Class #0            -

  Class descriptor  : 'La0;' # 原aaa/aaa

...

Class #1            -

  Class descriptor  : 'La1;' # 原bbb/ccc

...

Class #2            -

  Class descriptor  : 'La2;' # ...

...

Class #100            -

  Class descriptor  : 'La100;' # 平时用不到的aaa/bbb

...

经验总结

根据上述的流程,我们探讨了Dalvik Other和.dex mmap部分的内存,大致搞清楚了它们被消耗的机制,以及一些能够减少消耗的方法。经验如下:

在优化内存时,不只有堆内存,还有其他许多类型的内存能够进行分析和优化。

dex文件有很多优化空间。在仔细统计并调整了dex文件的顺序后,往往能够节约1MB以上的mmap内存。

引入SDK库和调用新的系统API时需要考虑成本。有可能一些不常用的功能会导致大量的内存消耗。这时有可能需要多进程方案,将这些影响内存的操作放入临时进程执行。

转载地址:http://yuxyx.baihongyu.com/

你可能感兴趣的文章
Angular.js中的$injector服务
查看>>
构建之法读书笔记01
查看>>
linux - lsof 命令最佳实践
查看>>
kafka性能测试
查看>>
现实世界的Windows Azure:h.e.t软件使用Windows Azure削减50%的成本
查看>>
深入.net框架
查看>>
聚合类新闻client产品功能点详情分析
查看>>
js设置定时器
查看>>
数据库除运算
查看>>
LeetCode--112--路径总和
查看>>
DeviceIOControl与驱动层 - 缓冲区模式
查看>>
感悟贴2016-05-13
查看>>
vim使用教程
查看>>
JDK在LINUX系统平台下的部署案例与总结
查看>>
跨vlan通信-----单臂路由技术
查看>>
百度编辑器ueditor 光标位置的坐标
查看>>
DEV-C++ 调试方法简明图文教程(转)
查看>>
VS2017+EF+Mysql生成实体数据模型(解决闪退的坑)
查看>>
C++多态、继承的简单分析
查看>>
库克称未来苹果用户可自己决定是否降频 网友:你是在搞笑吗?
查看>>