(安卓/android)dex2oat与应用安装时间优化

dex2oat与应用安装时间优化

背景

  • 4.4之前,android使用dalvik虚拟机,采用JIT(Just-in-time 即时编译),在运行时将字节码即时翻译成机器码再执行

  • 5.0开始,android使用art虚拟机,采用AOT(Ahead Of Time 运行前编译),在安装时将字节码(.dex)翻译成机器码(.oat)再执行,提高运行时效率

  • 由于dex2oat过程涉及 读取dex -> 以类为粒度编译 -> 生成许多中间文件 ->合并为.oat(odex)文件 所以EMMC性能 ,CPU性能, swap区大小, 等硬件性能在一定程度上决定了编译的速度, 而3561平台性能较弱,编译时间的增加直接的造成安装时间提高, 同时应用的热更新包编译会占据一定的cpu资源造成系统卡顿,直接影响用户体验,本文主要从灵活修改dex2oat调度策略来优化dex2oat过程,而没有减少dex2oat时间

优化

1.对与安装应用,根据应用的方法数来决定是否执行dex2oat

  • 如果对所有应用强制不进行dex2oat,能一劳永逸,将安装时间压缩到最低,但是这违背了art虚拟机设计的初衷

  • 虽然很多市场上的app体积比较大,但是除去资源文件,他的类数量和方法数量不一定像体积那么大,对于方法数,
    较小的应用,去做dex2oat的优化是有必要的,因为与dex2oat时间产生直接关系的是方法数而不是apk体积

  • 我们希望在3561平台上将所有应用的安装时间控制在1分钟以内,经过测试和统计,执行dex2oat在一分钟左右的应用方法数在110000左右,所以对于方法数在110000以下的应用执行dex2oat,对方法数在110000以上的应用禁止dex2oat,以达到性能和安装时间均衡的状态

commands.cpp
# 从property中获取dex2oat方法数边界值
bool have_dex2oat_num_dex_methods_flag = property_get("fly.dex2oat.num_dex_methods",
                                                              dex2oat_num_dex_methods_flag, NULL) > 0;
# 设置--num-dex-methods参数,
if(have_dex2oat_num_dex_methods_flag){
        sprintf(dex2oat_num_dex_method_arg, "--num-dex-methods=%s", dex2oat_num_dex_methods_flag);
    }


dex2oat.cc   
if (!image_ &&
      compiler_options_->IsCompilationEnabled()) {
      size_t num_methods = 0;
      for (size_t i = 0; i != dex_files_.size(); ++i) {
        const DexFile* dex_file = dex_files_[i];
        CHECK(dex_file != nullptr);
        num_methods += dex_file->NumMethodIds();
      }
      # GetNumDexMethodsThreshold()获取--num-dex-methods参数
if (num_methods != 0 
      && compiler_options_->GetNumDexMethodsThreshold() != 0 
      && num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
        # 方法数小于--num-dex-methods则设置为kSpeed,执行dex2oat
        compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
      }
else{  
        # 方法数大于--num-dex-methods则设置为kInterpretOnly,禁止dex2oat
    	compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly);
      }
    }

2.对与应用插件,限制对插件执行dex2oat的线程数

热更新不可能直接把java源码下发,只能下发dex文件,在本地把基线dex给反编译然后合并再回编译很明显不可能,毕竟baksmali.jar和smali.jar加起来都快5m了,热更新框架不可能搞这么大。但是利用dex2oat的这个特性,我们可以只下发包括更新代码的dex,客户端接收后把新dex作为classes.dex,旧dex作为classes2.dex,送进dex2oat,就能得到一个更新过代码的oat文件,尽可能简单的完成热更新。

作者:琴梨梨

链接:https://www.jianshu.com/p/cf63266cca86

插件下发时,dex2oat默认会开启和cpu核心数相同的线程数去编译,占据非常的的cpu资源,很容易会造成系统卡顿

插件一般会下载到APP的私有数据目录,即 “data/data/” 或 “data/user/” ,而使用dex2oat时会把dex的源路径作为参数传入,根据参数有无包含这两个路径可以判断是否是一个插件在调用dex2oat,如果是的话则限制dex2oat的线程数为1,保证热更新的正常和系统的流畅

 dex2oat.cc   
 for (const char* dex_file_name : dex_filenames_) {
	std::string fileNameStr(dex_file_name);
        if(fileNameStr.find("data/user/") != std::string::npos || fileNameStr.find("data/data/") != std::string::npos)        {
	  compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
	  # 限制线程数为1
          thread_count_ = 1;	
	  break;
	}
      }

你可能感兴趣的:(android)