64K问题
65535 方法数问题本质原理
产生 65535 问题的原因
单个 Dex 文件中,method 个数采用使用原生类型 short 来索引,即 2 个字节最多 65536 个 method,field、class 的个数也均有此限制,关于如何解决由于引用过多基础依赖项目,造成 field 超过 65535 问题
对于 Dex 文件,则是将工程所需全部 class 文件合并且压缩到一个 DEX 文件期间,也就是使用 Dex 工具将 class 文件转化为 Dex 文件的过程中, 单个 Dex 文件可被引用的方法总数(自己开发的代码以及所引用的 Android 框架、类库的代码)被限制为 65536。
这就是 65535 问题的根本来源。
LinearAlloc 问题的原因
这个问题多发生在 2.x 版本的设备上,安装时会提示 INSTALL_FAILED_DEXOPT
,这个问题发生在安装期间,在使用 Dalvik 虚拟机的设备上安装 APK 时,会通过 DexOpt 工具将 Dex 文件优化为 ODex 文件,即 Optimised Dex,这样可以提高执行效率。
在 Android 版本不同分别经历了 4M/5M/8M/16M 限制,4.2.x 系统上可能都已到 16M,在 Gingerbread 或以下系统 LinearAllocHdr 分配空间只有 5M 大小的, 高于 Gingerbread 的系统提升到了 8M。Dalvik linearAlloc 是一个固定大小的缓冲区。dexopt 使用 LinearAlloc 来存储应用的方法信息。Android 2.2 和 2.3 的缓冲区只有 5MB,Android 4.x 提高到了 8MB 或 16MB。当应用的方法信息过多导致超出缓冲区大小时,会造成 dexopt 崩溃,造成 INSTALL_FAILED_DEXOPT 错误。
本质
65535 = 2^16-1,也称之为 64K 方法数问题。指的是 Android Dalvik 可执行文件.dex 中的 Java 方法数引用超过 65535。
Android Apk 本质是一个压缩文件,里面包含的 classes.dex 文件是可执行的 Dalvik 字节码文件,存放的是所有编译后的 Java 代码;Dalvik 可执行文件规范限制了单个.dex 文件最多能引用的方法数是 65536 个(最初设计是错误的),这其中包含了 Android Framework、APP 引用的第三方库及 APP 自身的方法数。
表现
表现在构建 App 时的编译错误,导致构建失败。
- 旧版构建系统报错
1
2
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
- 新版构建系统报错
1
2
3
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
1
The number of method references in a .dex file cannot exceed 64K
解决
Android5.0(API21) 之前
Android5.0(API21) 之前系统使用的是 Dalvik 虚拟机,默认情况下 Dalvik 为每个 apk 只生成一个 classes.dex 文件。解决方法是,拆分成多个 classesxxx.dex,先加载主 classes.dex,然后加载从.dex。或者用 google 提供的MultiDex support 库。
Android5.0+
Android5.0+ 使用的是 ART 虚拟机替代了 Dalvik 虚拟机,ART 支持从 apk 中加载多个.dex 文件,在应用安装期间,它会执行一个预编译操作,扫描 apk 中的 xxx.dex 文件并将它们编译成一个单一的 .oat
文件,在应用运行时去加载这个.oat 文件,而不是一个一个的加载.dex 文件。
MultiDex 局限
- 影响效率,构建系统需要经过复杂的计算决定哪些类放在
主dex
,哪些类放从dex
。解决就是开发阶段最低使用 api21,也就是 art 就快一些。 - 应用首次启动 Dalvik 虚拟机会对所有的.dex 文件执行
dexopt
操作,生成ODEX
文件,这个过程很复杂且非常耗时,如果应用的从dex
文件过大,可能会导致 ANR 出现。 - Android4.0 以下,Dalvik linearAlloc 的 bug(http://b.android.com/22586),使用 MultiDex 的应用可能启动会失败。>
- Android5.0 以下,由于 Dalvik 的线性内存分配器
linearAlloc
的限制 (http://b.android.com/78035),使用 MultiDex 的应用在出现很大的内存分配时,可能会导致应用崩溃。根本原因是 Dalvik 虚拟机用来加载类的堆内存大小被硬编码了,Android2.3 以下是 5M,Android2.3 是 8M,Android4.0 为 16M,但在 Android5.0 以下,还是可能会超过这个限制,从而导致崩溃,当然 Android5.0+ 使用了 ART 虚拟机也就不存在 linearAlloc 的问题了。> - 引入 MultiDex,就存在主从 dex 文件。启动所需要的类都必须放在主 dex 文件中,否则会出现
NoClassDefFoundError
的错误。Android 构建工具自动帮我们处理了 Android 系统相关的依赖;但对于应用自己引入的第三方库,如果还依赖其他的一些东西,如通过反射调用的 Java 类,或者通过 NDK 代码层的 Java 方法,可能就不会被放到主 dex 文件中,如果在应用启动时需要用到,就会出现NoClassDefFoundError
错误。
Reference
https://developer.android.com/intl/zh-cn/tools/building/multidex.html
关于『65535 问题』的一点研究与思考
http://blog.csdn.net/zhaokaiqiang1992/article/details/50412975