Android Dalvik 仮想マシンの紹介

bd464143f4a3145d8c53b039309a2e40.gif

一生あなたと一緒に学びましょうXi、こちらはプログラマー Android です

おすすめの定番記事. この記事を読むことで以下の知識が得られます。

1. Android での Java 言語実行プロセス
2. 仮想マシン開発プロセス
3. Android Dalvik モード
4. Android N における dex2oat の原理とモード
5. dex2oat で使用される関連パラメータの判断方法
6. dex2oat のログの表示方法
7 . いつ行うべきか dex2oat
8. 携帯電話の応答が遅い原因
9. 携帯電話の応答が遅い場合の解決方法
10. 参考文献

1. Java言語はAndroid上でプロセスを実行します

7f82d6ae38d802efa8308b76375ae590.jpeg

Java言語はAndroid上でプロセスを実行します

2. 仮想マシンの開発プロセス

  • 1.初期Dalvik

  • 2.中期ダルヴィク

  • 3. 後期ARTモード

    APK をさまざまな仮想マシンで実行できるようにするために、Google はアダプター モードを採用し、仮想マシンを実行する前に dexopt を実行します。つまり、dex ファイルを odex ファイルに最適化して、仮想マシンをより最適に実行できるようにします。

    ART 仮想マシンでは、dexopt は dex ファイルをバイナリ形式に最適化し、ART 仮想マシンが実行できるようにします。
    Dexopt は、最適化のために dex2oat を呼び出します。dex2oat のタスクは、元の dex ファイルを事前変換することで、アプリの実行時間を短縮できます。ただし、一部のアプリは複雑であるため、最適化にかかる時間は比較的長くなります。
    最適化は、dex ファイルのメソッド メソッドに基づいています。dex2oat が最適化するとき、必要に応じて一定量のメソッドが最適化されます。つまり、すべてのメソッドが OAT モードに変換されるわけではありません。

3.Android Dalvikモード

メソッド量に対して最適化されるポイント数に基づくモード

62c0a6a31f15b84994e85bcbf0151825.jpeg

アンドロイド・ダルヴィク

4. Android Nにおけるdex2oatの原理とモード

NバージョンではJITモードが強化されました。JITモードとは、Just in timeの略です。これは、実行時に、メソッドの使用頻度に基づいて、特定のメソッドを最適化するかどうかが決定されることを意味します。仮想マシンは各メソッドの実行回数をカウントします。メソッドが何度も実行され、特定のしきい値に達すると、そのメソッドはホット メソッドにアップグレードされ、プロファイルに記録されます。これらのメソッドは、システムがアイドル状態で充電中の場合にのみ最適化されます。これらのメソッドは実行時にも使用しやすくするために最適化されます。

  • 1.Android N dex2oat モード図

ae37535b1613d6d83281d871f7b91740.jpeg

Android N dex2oat パターン図

  • 2.Android N dex2 オートの概略図

16cfdcac91cbd24b463de7b68c306d01.jpeg

Android N dex2oat の概略図

5. dex2oat で使用される関連パラメータを決定する方法

dex2oat のパラメータは、次の関数を通じてコードに出力されます。

  • 1.Lバージョン

L版は無条件で印刷されます

timings.StartTiming("dex2oat Setup");
1436  LOG(INFO) << CommandLine();
  • 2. M バージョンと N バージョン

MとNでは条件により異なる内容が印刷されます。
たとえば、非常に詳細な dex2oat パラメータはデバッグ バージョンで出力され、コンテンツの一部は非デバッグ バージョンで出力されます。

if (kIsDebugBuild || dex2oat.IsImage() || dex2oat.IsHost() || !kIsTargetBuild) {
2144    LOG(INFO) << CommandLine();
2145  } else {
2146    LOG(INFO) << StrippedCommandLine();
2147  }

6. dex2oatのログの見方

キーワードを検索する

  • デックス2オート

  • dex-file
    に最適化された dex ファイルの情報

  • オーツ麦-FD

  • オート麦の場所
    オート麦の保管場所

  • コンパイラ フィルタは、
    使用する最適化モードを決定します

例1:

01-01 04:28:03.471265  5654  5654 I dex2oat : Starting dex2oat.

01-01 04:28:37.548439  5654  5654 I dex2oat : dex2oat took 34.077s (threads: 4) arena alloc=23MB java alloc=14MB native alloc=88MB free=5MB 01-01

其中:dex2oat tokk 34.077s表示 dex2oat的耗时情况,(threads:4)表示,一共用了4个线程做这件事情。

例2:

01-01 08:34:27.063 I/dex2oat ( 7714): /system/bin/dex2oat --runtime-arg -classpath 
--runtime-arg --instruction-set=arm --instruction-set-features=default --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art 
--dex-file=/data/data/com.tencent.mm/app_dex/secondary-2.dex.jar --oat-fd=62 --oat-location=/data/data/com.tencent.mm/app_cache/secondary-2.dex.dex 
--runtime-arg -Xms64m --runtime-arg -Xmx512m  --compiler-filter=speed

M 和 N 版本,可以通过会打印出较多的dex2oat参数。

上面的Log中,可以得出很多信息来:
1.被优化的dex文件信息,可以通过 --dex-file获取,形如上面我们就知道优化的是微信里面的内容,因为--dex-file参数的dex文件位于
   com.tencent.mm目录下面的。
2.优化之后oat存储的位置,可以看到--oat-location其是保存在包名下面的app_cache目录下面的。
3.是对插件优化还是对APK本身优化。在L的时候所有的PMS优化的内容都会放在data/dalvik-cache目录下面,所以如果oat放的位置是data/
  dalvik-cache目录下面,那么就是优化主APK,否则就是优化插件的。
  而在M和N当中,优化的结果会放在自己的包下面,但是不会是这种类似app_cache这种,根据这个可以判断是对插件的优化。
4.优化采用的模式
   根据--compiler-filter可以判断采用的是哪种模式。

如果我们关注的信息没有打印出来,可以修改上面打印这些Log的条件。

7. dex2oat をいつ実行するか

dex2oat は通常、次の場合に発生します。

  • 初めての起動

当第一次开机的时间,PackageManagerService会扫描手机中的各个目录,
当检测到APK之后,就会执行安装流程,在安装的时候,就会对这些APP进行dex2oat操作。
如果说APP比较多,或者dex2oat比较慢,那么开机时间就会比较长。
  • アプリをインストールする

当系统开机之后,我们安装APP的时候,也会进dex2oat。
实质上这时候也是走到了PackageManagerService的流程。
由PMS执行安装的运行,并发起dex2oat的动作。
  • 最適化プラグイン

当APP运行的时候,会根据自己的需要,优化自己需要的插件文件。
例如自己引用到的非系统类的jar包。

要約すると、システムが dex2oat を実行する主な理由は 2 つあり、1 つはシステム サーバーの Packagemanagerservice によって開始され、installd によって実行され、もう 1 つは APP 自体がニーズに応じて独自のプラグインを最適化することです。

8. 携帯電話の反応が遅い原因

遅い原因は以下の通りです

  • ワークロード
    dex2oat が発生すると、元ファイル内の dex ファイルが抽出され、各命令が判断されて翻訳され、大量の中間コンテンツが生成されます。そして、利用可能なすべての CPU を占有します (現在の戦略は、コアと同じ数のスレッドを開始することです)

  • CPU の数
    が多いほど、より多くのスレッドが開始され、タスクはより速く完了します。

  • EMMC の
    dex ファイルが比較的大きい場合、メモリに保存できない大量の中間コンテンツが生成されるため、スワップ メカニズムを使用して一部のコンテンツを EMMC に交換し、大量の読み取り操作が発生します。結果は同時に emmc に保存されるため、emmc のパフォーマンスも非常に重要です。

  • メモリ
    前述したように、メモリが少ないほどスワップが容易になり、IO ボトルネックが発生します。

  • パッチ
    Google では以前に最適化が行われていました。つまり、dex2oat が完了しても、破棄されないようにしました。これは、破棄すると交換されたコンテンツが再度読み取られるため、時間がかかります。
    バージョン 1.M のパッチは次のとおりです:
    ALPS02736915
    バージョン 2.N のパッチはまだありません。

9. 携帯電話の応答が遅い場合の解決方法

遅い問題が発生した場合は、まず上記の側面から原因を探し、解決できない場合は、APP のインストール戦略を調整する必要があります。たとえば、最初はスピード モードでしたが、通訳専用モードに変更されました。
参照の変更方法は次のとおりです。

  • 1. N バージョン (L /M は適用不可) PMS にホワイトリストを追加

/frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java

private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter) {


//在这函数中,可以判断传递下来的pkg是否是我们需要的package,如果是的话,将targetCompilerFilter设置为speed-profile。

//speed-profile会在安装的时候采用interperter-only,然后,运行一段时间之后,会将那些常用的方法优化成为speed模式。也就是说是有选择性的优化。

 if (isProfileGuidedFilter && isUsedByOtherApps(pkg)) {
 checkProfiles = false;

 targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
 if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
 throw new IllegalStateException(targetCompilerFilter);
 }
 isProfileGuidedFilter = false;
}

// If we're asked to take profile updates into account, check now.
boolean newProfile = false;


###  mtk add begin
 if(pkg.contains("com.tencent.mm")){
 targetCompilerFilter="interpret-only";
 }
### mtk add end
  • 2. Installd のホワイトリスト処理は
    LMN バージョンの
    実装アイデアに適しています

//1.通过 property来增加白名单
//注意Facebook是apk的名字,后面不用带后缀,但是大小写一定要匹配。
adb shell setprop ro.mtk.dex2oat_white_list Facebook:

//2.如果需要增加到开机启动当中,可以如下修改在device.mk中加入
//多个APK名字之间用冒号隔开。
PRODUCT_PROPERTY_OVERRIDES += ro.mtk.dex2oat_white_list=facebook:Facebook:youtube:skype:twitter:

変更点
\frameworks\native\cmds\installd\commands.c

static int shouldUseInterpretonly(const char* input_file_name);

static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
const char* output_file_name, int swap_fd, const char *pkgname, const char *instruction_set,
bool vm_safe_mode)
{

......

######## MTK add start
//适当的时候,进行调用

if(shouldUseInterpretonly(input_file_name))
{
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
have_dex2oat_compiler_filter_flag = true;   //关键是设置这个参数的值
ALOGW("%s is in whitelist from property so set interpret-only",input_file_name);
}
######## MTK add end
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);

.....

}
######MTK add start
/**
*函数功能,根据input的file name判断,当前来优化的APK名字判断是否在白名单当中。
*白名单来自于property
*/
static int shouldUseInterpretonly(const char* input_file_name)
{
char prop_buf[PROPERTY_VALUE_MAX];
memset(prop_buf,0,PROPERTY_VALUE_MAX);
bool have_whitelist = property_get("ro.mtk.dex2oat_white_list", prop_buf, NULL) > 0;
if(!have_whitelist)
return false;
char *str = prop_buf;
char appname[128],*ptrname = appname;
memset(appname,0,128);

while(*str)
{
if(*str != ':')
{
*ptrname = *str;
ptrname ++;
str++;
}
else{

str++;

if(*appname != 0)
{
if(strstr(input_file_name,appname)) //found
{
return 1; 
}
else
{
memset(appname,0,sizeof(appname));
ptrname = appname;
}
}
}
}
return 0;
}
### MTK add end
  • 3. dex2oat.ccでホワイトリスト処理を実行します。

/art/dex2oat/dex2oat.cc
プラグインは PMS と installd のパスをたどらないため、これをインターセプトする方法は dex2oat でのみ見つかります。ここで解決する必要がある問題は、関心のあるプラグインをどのように判断して最適化するかということです。

  • dex2oatプロセスの親プロセスのpidを判断してプラグインかどうかを判断する
    dex2oatがプラグインで実行される場合、そのプロセスの親プロセスがインストールされないのが特徴です。ですので、1点だけで判断することも可能です。ただし、すべてのプラグインをインタープリターのみに最適化する機能があります。

N バージョンを例として、推奨されるコードは次のとおりです。

/*
 *调整编译的模式,规则:如果当前进程的父进程是installd,不做修改,否则修改为interpreter-only。
 *因为插件类的,都是进程本来fork的dex2oat.
 *only for device end
 *
 */
 void adjustCompilerOptions(const CompilerOptions* compiler_options){
 
 int ppid = getppid(); //获取当前进程的parent pid
 char cmdline_path[256];
 char contents[256];
 memset(cmdline_path,0,sizeof(cmdline_path));
 memset(contents,0,sizeof(contents));
 snprintf(cmdline_path,"/proc/%d/cmdline");
 FILE *fd = fopen(cmdline_path,"r"); // 打开proc/pid/cmdline文件,以读取对应ppid进程的名字
 
 if(fd == NULL){
 LOG(ERROR) < return;
 }
 
 fgets(contents,sizeof(contents),fd); //读取文件中的内容
 fclose(fd);
 if(strstr(contents,"installd")){ //判断是否是installd
 return;
 }
 
 Log(INFO)<<"parent is not installd,may be is a plugin adjust it to interpret-only";
 compiler_options->SetCompilerFilter(kInterpretOnly); //如果是的话,强行调整编译模式为interpreteronly模式
 
 
 }

在ParseArgs函数中对dex2oat的模式进行调整:
 ProcessOptions(parser_options.get());

 // Insert some compiler things.
 InsertCompileOptions(argc, argv);
 
 if(!IsHost())  //注意一定要判断非host端,否则会有问题
 adjustCompilerOptions(compiler_options_.get());
 
 }
  • dexファイル名またはoatlocation名に基づいて判断します

L版本:
/art/dex2oat/dex2oat.cc

方法1:

// mtk add begin

if ((!oat_filename_.empty() && (oat_filename_.find("facebook") != std::string::npos))||(!zip_location_.empty() && (zip_location_.find("facebook") !=std::string::npos))||((!oat_location_.empty()) && oat_location_.find("facebook") != std::string::npos)){  // mtk modify 2016-06-06

                                    compiler_filter_string = "interpret-only";

                                    LOG(INFO) <<" This apk is in whitelist from property so set interpret-only ";

            }

// mtk add end

if (compiler_filter_string = nullptr){

      compiler_filter_string = "speed";

}

 注意:CTS测项用的包不能使用interpret-only模式,因为一些CTS测项是性能相关的,如果改为interpret模式,会导致CTS不过。之前有客户为了追求更快的安装速度,将所有的包都改为interpret-only的,结果CTS就不过了,针对这种情况,可以把CTS的包再放到speed模式的白名单当中。cts的关键字为test.

 

方法2(若上述方法无效):

1420 if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) {
1421 compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed);
1422 VLOG(compiler) << "Below method threshold, compiling anyways";
1423 }
1424 }
1425
1426//mtk_add_begin
1427 static constexpr size_t kMinCompileDexSize = 4;
1428 if (!image_ && dex_files_.size() > kMinCompileDexSize) {
1429 compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly);
1430 LOG(INFO) << "Enable Whitelist Rules. Current Dex File Sizes:" << dex_files_.size();
1431 }
1432//mtk_add_end
1433
1434 return true;
1435 }
1436
1437 // Create and invoke the compiler driver. This will compile all the dex files.
1438 void Compile() {
1439 TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
1440 compiler_phases_timings_.reset(new CumulativeLogger("compilation times"))

または次のように

[SOLUTION L版本]

    在device.mk中加入PRODUCT_PROPERTY_OVERRIDES += ro.mtk.dex2oat_white_list=com.tencent.mm: (注意 包名后又冒号“:”
 
[添加source code](抱歉不能排版)
 /art/dex2oat/dex2oat.cc添加红色部分:
 
 #ifdef HAVE_ANDROID_OS
 extern "C"{
                  static int shouldUseInterpretonly(const char* filename){
                       char prop_buf[92];
                       memset(prop_buf,0,92);
                       bool have_whitelist = property_get("ro.mtk.dex2oat_white_list", prop_buf, NULL) > 0;
                       if(!have_whitelist)
                       return false;
                       char *str = prop_buf;
                       char appname[128],*ptrname = appname;
                       memset(appname,0,128);

                       while(*str)
{
         if(*str != ':'){
                                     *ptrname = *str;
                                      ptrname ++;
                                      str++;
              } else{
                                       str++;
                                      if(*appname != 0){
                                                   if(strstr(filename,appname)) //found
                                                     {
                                                              return 1; 
                                                      } else{
                                                              memset(appname,0,sizeof(appname));
                                                              ptrname = appname;
                                                      }
                                         }
                         }
}

return 0;
}
}
#endif

static int dex2oat(int argc, char** argv) {

          std::string dex_filename; //mtk_add
           ......
         if (option.starts_with("--dex-file=")) {
                    dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
                    dex_filename = option.substr(strlen("--dex-file=")).data(); //mtk_add
         } else if......

         
         if (compiler_filter_string == nullptr) { 
         if (instruction_set == kMips64) {
         // TODO: fix compiler for Mips64.
                     compiler_filter_string = "interpret-only";
         } else if (image) {
                     compiler_filter_string = "speed";
         } else {
                    #if ART_SMALL_MODE
                    compiler_filter_string = "interpret-only";
        #else
          #ifdef HAVE_ANDROID_OS
                 if(shouldUseInterpretonly(zip_location.c_str())){
                              compiler_filter_string = "interpret-only";
                              LOG(INFO) <<" This apk is in whitelist from property so set interpret-only";
                  }else if(shouldUseInterpretonly(dex_filename.c_str())){
                              compiler_filter_string = "interpret-only";
                              LOG(INFO) <<" This jar is in whitelist from property so set interpret-only";
                   }else{
          #endif
                               compiler_filter_string = "speed";
           #ifdef HAVE_ANDROID_OS
                    } 
        #endif
#endif 
    }
}

CHECK(compiler_filter_string != nullptr);
.......

}

参考文献:

[Tencent ドキュメント] Android フレームワーク ナレッジベース
https://docs.qq.com/doc/DSXBmSG9VbEROUXF5

フレンドリーなおすすめ:

Android開発に役立つ情報を集めました。

この時点で、この記事は終了です。編集者は、インターネットから転載された記事が優れていると考えています。クリックして元の記事を読み、元の著者をサポートしてください。権利侵害がある場合は、編集者に連絡して削除してください。ご提案や修正を歓迎します。ご注目をお待ちしております。読んでいただきありがとうございます。ありがとうございます!

3a2996a13a3aafb7b0838b1c2e4168dd.jpeg

クリックして元記事を読んで、ボスにいいね!

おすすめ

転載: blog.csdn.net/wjky2014/article/details/131842726