为什么Android下的注释会出现这样的性能问题(慢)?

我是ORMLite的主要作者,它在类上使用Java注释来构建数据库模式。对于我们的软件包来说,一个很大的启动性能问题是在Android 1.6下调用注释方法。在3.0之前,我看到了相同的行为。

我们看到以下简单的注释代码非常耗费GC,并且是一个真正的性能问题。在快速的Android设备上,对注释方法的1000次调用几乎需要一秒钟。在我的Macbook Pro上运行的相同代码可以同时进行2800万次(原文如此)呼叫。我们有一个注释,其中包含25种方法,我们希望每秒完成其中的50多个方法。

有谁知道为什么会发生这种情况,是否有任何解决方法?在缓存这些信息方面,ORMLite当然可以做一些事情,但是我们可以做些什么来“修复”Android下的注释吗?谢谢。

public void testAndroidAnnotations() throws Exception {
    Field field = Foo.class.getDeclaredField("field");
    MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
    long before = System.currentTimeMillis();
    for (int i = 0; i < 1000; i++)
        myAnnotation.foo();
    Log.i("test", "in " + (System.currentTimeMillis() - before) + "ms");
}
@Target(FIELD) @Retention(RUNTIME)
private static @interface MyAnnotation {
    String foo();
}
private static class Foo {
    @MyAnnotation(foo = "bar")
    String field;
}

这将导致以下日志输出:

I/TestRunner(  895): started: testAndroidAnnotations
D/dalvikvm(  895): GC freed 6567 objects / 476320 bytes in 85ms
D/dalvikvm(  895): GC freed 8951 objects / 599944 bytes in 71ms
D/dalvikvm(  895): GC freed 7721 objects / 524576 bytes in 68ms
D/dalvikvm(  895): GC freed 7709 objects / 523448 bytes in 73ms
I/test    (  895): in 854ms

编辑:

在@candrews给我指出正确的方向之后,我对代码进行了一些研究。性能问题看起来是由 中的一些可怕的粗略代码引起的。它调用这两种方法,然后比较它们。每次使用一堆追加方法,没有良好的初始化大小。通过比较字段来执行操作将明显更快。Method.equals()toString()toString()StringBuilder.equals

编辑:

一个有趣的反射性能改进给了我。现在,我们使用反射来查看类内部,以直接读取字段列表。这使得反射类对我们来说快了20,因为它绕过了使用调用的调用。它不是一个通用的解决方案,但这里是来自ORMLite SVN存储库的Java代码。有关通用解决方案,请参阅下面的yanchenko的答案AnnotationFactorymethod.equals()


答案 1

谷歌已经承认了这个问题,并在“蜂巢后”修复了它。

https://code.google.com/p/android/issues/detail?id=7811

因此,至少他们知道它,并且据说已经为将来的版本修复了它。


答案 2

以下是Gray'suser931366的想法的通用版本:

public class AnnotationElementsReader {

    private static Field elementsField;
    private static Field nameField;
    private static Method validateValueMethod;

    public static HashMap<String, Object> getElements(Annotation annotation)
            throws Exception {
        HashMap<String, Object> map = new HashMap<String, Object>();
        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
        if (elementsField == null) {
            elementsField = handler.getClass().getDeclaredField("elements");
            elementsField.setAccessible(true);
        }
        Object[] annotationMembers = (Object[]) elementsField.get(handler);
        for (Object annotationMember : annotationMembers) {
            if (nameField == null) {
                Class<?> cl = annotationMember.getClass();
                nameField = cl.getDeclaredField("name");
                nameField.setAccessible(true);
                validateValueMethod = cl.getDeclaredMethod("validateValue");
                validateValueMethod.setAccessible(true);
            }
            String name = (String) nameField.get(annotationMember);
            Object val = validateValueMethod.invoke(annotationMember);
            map.put(name, val);
        }
        return map;
    }

}

我已经用4个元素对注释进行了基准测试。
10000 次迭代的毫秒时间,获取所有这些迭代的值或调用上述方法:

     Device        Default  Hack
HTC Desire 2.3.7    11094   730
Emulator 4.0.4      3157    528
Galaxy Nexus 4.3    1248    392

以下是我如何将其集成到DroidParts中https://github.com/yanchenko/droidparts/commit/93fd1a1d6c76c2f4abf185f92c5c59e285f8bc69


推荐