在 Android Runtime (ART) 中验证应用行为

Android Runtime (ART) 是运行 Android 的设备的默认运行时 5.0(API 级别 21)及更高版本。此运行时提供多种功能 提升 Android 平台和应用的性能和流畅度。 要详细了解 ART 的新功能,请参阅简介 ART

不过,一些适用于 Dalvik 的技术不适用于 ART。这个 文档会告知您在迁移现有 才能与 ART 兼容。大多数应用应该仅在 ART。

解决垃圾回收 (GC) 问题

在 Dalvik 中,应用经常发现明确调用 System.gc(),用于提示垃圾回收 (GC)。此值应为 对 ART 的必要性大大降低,尤其是在您调用垃圾回收时 以防止出现 GC_FOR_ALLOC 类型 或减少分片。您可以验证正在使用的运行时 调用 System.getProperty("java.vm.version")。如果使用的是 ART,则该属性的值 为 "2.0.0" 或更高版本。

ART 使用并发复制 (CC) 收集器,可并发压缩 Java 堆。 因此,您应该避免使用 与紧凑型 GC 不兼容(例如,保存指向对象的指针) 实例数据)。这对于使用 Java 原生接口 (JNI)。如需了解详情,请参阅预防 JNI 问题

预防 JNI 问题

ART 的 JNI 比 Dalvik 的 JNI 更严格一些。这是一个特别的好主意 使用 CheckJNI 模式来捕获常见问题。如果您的应用使用 C/C++ 请查看以下文章:

调试 将 Android JNI 与 CheckJNI 搭配使用

检查 JNI 代码中的垃圾回收问题

并发复制 (CC) 收集器可能会移动内存中的对象以进行压缩。 如果您使用 C/C++ 代码,请不要 执行与紧凑型 GC 不兼容的操作。我们改进了 CheckJNI 以识别一些潜在问题(如 JNI ICS 中的本地参考文件变更)。

需要特别注意的一个方面是 Get...ArrayElements()Release...ArrayElements() 函数。在使用非紧凑型 GC 的运行时中, Get...ArrayElements() 函数通常会返回对 支持数组对象的实际内存。如果您对其中的某个属性进行了更改 数组元素,数组对象本身会发生更改(以及参数 对 Release...ArrayElements() 的处理通常会被忽略)。但是,如果 则 Get...ArrayElements() 函数可能 返回该内存的副本如果在压缩 GC 时滥用引用, 使用,这可能会导致内存损坏或出现其他问题。例如:

  • 如果您对返回的数组元素进行任何更改,则必须在 相应的 Release...ArrayElements() 函数, 以确保您所做的更改已正确复制回 数组对象。
  • 释放内存数组元素时,必须使用相应的 模式: <ph type="x-smartling-placeholder">
      </ph>
    • 如果您没有对数组元素进行任何更改,请使用 JNI_ABORT 模式,在不复制数据的情况下释放内存 更改回底层数组对象。
    • 如果您更改了数组,并且不需要任何引用, 另外,请使用代码 0(它会更新数组对象并释放 回忆的副本)。
    • 如果您对要提交的数组进行了更改,并且希望 来保留数组的副本,请使用 JNI_COMMIT(更新 底层数组对象并保留副本)。
  • 调用 Release...ArrayElements() 时,系统会返回相同的 最初由 Get...ArrayElements() 返回的指针。对于 递增原始指针(扫描 数组元素),然后将递增的指针传递到 Release...ArrayElements()。传递此修改后的指针可能会导致 要释放的错误内存,从而导致内存损坏。

错误处理

ART 的 JNI 会在多种情况下抛出错误,而 Dalvik 则不然。( 同样,您可以使用 CheckJNI 进行测试,以捕获许多此类情况。)

例如,如果使用如下方法调用 RegisterNatives: 不存在(可能是因为该方法已被 ProGuard),则 ART 现在会正确抛出 NoSuchMethodError

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

如果 RegisterNatives 满足以下条件,ART 也会记录错误(在 logcat 中可见) 不使用任何方法调用:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

此外,JNI 函数 GetFieldID()GetStaticFieldID() 现在可以正确抛出 NoSuchFieldError 而不是直接返回 null。同样,GetMethodID()GetStaticMethodID() 现在可以正确抛出 NoSuchMethodError。 这可能会导致 CheckJNI 由于未处理的异常或 抛出异常。这样, 使用 CheckJNI 模式测试与 ART 兼容的应用尤为重要。

ART 要求用户使用 JNI CallNonvirtual...Method() 方法 (例如 CallNonvirtualVoidMethod()),以便使用方法的声明 类,而不是子类。

预防堆栈大小问题

Dalvik 为原生代码和 Java 代码使用单独的堆栈,并使用默认的 Java 堆栈大小为 32KB,默认的原生堆栈大小为 1MB。ART 采用统一的 堆栈以改善局部性。通常,ART Thread 堆栈 大小应该与 Dalvik 大小大致相同。不过,如果您明确指定 设置堆栈大小,那么您可能需要重新检查 ART。

  • 在 Java 中,查看对指定显式堆栈的 Thread 构造函数的调用 。例如,如果发生 StackOverflowError,您将需要增加大小。
  • 在 C/C++ 中,复习 pthread_attr_setstack() 的用法和 pthread_attr_setstacksize(),适用于通过 JNI。以下是应用尝试调用 JNI 时记录的错误示例 AttachCurrentThread()
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

对象模型更改

Dalvik 错误地允许子类替换软件包私有方法。 在这类情况下,ART 会发出警告:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

如果您打算替换其他软件包中某个类的方法,请在 为 publicprotected

Object 现在包含私有字段。反思字段的应用 的类层次结构中,应注意不要试图查看 Object 的字段。例如,如果您要对某个类进行向上迭代, 作为序列化框架的一部分,

Class.getSuperclass() == java.lang.Object.class

而不是继续操作,直到该方法返回 null

现在,如果未发出任何请求,代理 InvocationHandler.invoke() 会收到 null 参数,而不是空数组。之前记录过这种行为, 在 Dalvik 中未正确处理。旧版 Mockito 难以处理 因此在使用 ART 进行测试时,请使用更新后的 Mockito 版本。

修复 AOT 编译问题

ART 的预先 (AOT) Java 编译应适用于所有标准 Java 代码。编译由 ART 的 dex2oat 工具;如何解决 dex2oat,请告诉我们(请参阅报告问题),以便我们尽快解决这些问题 。需要注意的几个问题:

  • ART 会在安装时执行比 Dalvik 更严格的字节码验证。 由 Android 构建工具生成的代码应该没有问题。不过, 后处理工具(尤其是执行混淆的工具)可能会产生 Dalvik 容忍但 ART 拒绝的无效文件。我们 与工具供应商合作,找出并解决此类问题。在许多情况下 最新版本的工具以及重新生成 DEX 文件可以解决这些问题 问题。
  • ART 验证程序标记的一些典型问题包括: <ph type="x-smartling-placeholder">
      </ph>
    • 无效的控制流
    • 不平衡的 monitorenter/monitorexit
    • 0 长度参数类型列表大小
  • 某些应用依赖于已安装的 .odex 文件 格式为 /system/framework/data/dalvik-cache 或 位于 DexClassLoader 的优化输出目录中。这些 文件现在是 ELF 文件,而不是 DEX 文件的扩展形式。ART 在尝试 遵循与 Dalvik 相同的命名和锁定规则,应用不得依赖于 文件格式格式可能会有变更,恕不另行通知。

    注意:在 Android 8.0(API 级别 26)和 DexClassLoader 优化的输出目录 已弃用。有关详情,请参阅 DexClassLoader() 构造函数。

报告问题

如果您遇到任何并非由应用 JNI 问题导致的问题,请报告 通过 Android 开源项目问题跟踪器(网址为 https://code.google.com/p/android/issues/list)提交问题。 在 Google"adb bugreport" Play 商店(如果有)。否则,如果可能,请附加可重现该问题的 APK。请注意,这些问题(包括附件)都是公开的 可见。