DalvikバイトコードのMethod数65k制限について
Androidアプリケーションにはアプリケーションのメソッド数が65kを超えられないという制限があるのだけど、その詳細を知らなかったので調べた。
参考: Does the Android ART runtime have the same method limit limitations as Dalvik?
まとめると
- メソッド数制限の実体は(VMとしての)DalvikではなくDalvik bytecodeのinvoke系で使うmethod idが16bit intであること
- つまり、アプリケーションで定義したメソッド数が65kを超えるかどうかではなく、ひとつのdex fileで参照しているメソッド数が65kを超えるときに問題が起きる
- ARTもDalvik bytecodeを一旦介する以上、同じ制限をもつ
という感じか。さらに手元で試してみたところ、問題が起きるケースではdx(1)が例外を吐くので、そもそもビルドが通らなくなる。メソッド数Android 2.3系でのみインストールできなくなるという問題があったと思ったが、最近のツールだとそもそもビルドが通らなくなるのでapkのなかのメソッド数をCIで数えてチェックする必要はないのかもしれない。
以下検証実験:
まず、ひとつのdexでメソッドを沢山定義するとdx(1)が以下のようなエラーを報告する。
https://github.com/gfx/Android-TooManyMethodsInDex/tree/over65k-definitions
$ ./gradlew assembleDebug (snip) :app:dexDebug trouble writing output: Too many method references: 65569; max is 65536. You may try using --multi-dex option. References by package: 2 android.app 1 android.util 1 android.widget 65559 com.example.gfx.over65methods 6 java.lang :app:dexDebug FAILED
ひとつのdexでの定義数を65k未満に抑えつつ、Android frameworkのメソッドを参照してmethod ref idが65kを突破したケースも同様:
https://github.com/gfx/Android-TooManyMethodsInDex/tree/by-external-refs
:app:dexDebug trouble writing output: Too many method references: 65537; max is 65536. You may try using --multi-dex option. References by package: 2 android.app 5 android.util 1 android.widget 65523 com.example.gfx.over65methods 6 java.lang :app:dexDebug FAILED
外部ライブラリによってメソッド数が増えすぎる場合、以下のような例外が出ることもあるようだ:
https://github.com/gfx/Android-TooManyMethodsInDex/tree/method-id-not-in-range
:app:dexDebug UNEXPECTED TOP-LEVEL EXCEPTION: java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536 at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501) at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:276) at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490) at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167) at com.android.dx.merge.DexMerger.merge(DexMerger.java:188) at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439) at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287) at com.android.dx.command.dexer.Main.run(Main.java:230) at com.android.dx.command.dexer.Main.main(Main.java:199) at com.android.dx.command.Main.main(Main.java:103) :app:dexDebug FAILED