首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么Android下的注解会出现这样的性能问题(缓慢)?

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

Stack Overflow用户
提问于 2011-09-14 21:49:51
回答 4查看 12.4K关注 0票数 50

我是ORMLite的主要作者,它在类上使用Java来构建数据库模式。我们的包的一个很大的启动性能问题是在Android1.6下调用注解方法。我在3.0版本中看到了同样的行为。

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

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

代码语言:javascript
复制
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;
}

这将导致以下日志输出:

代码语言:javascript
复制
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()都使用带有一堆append方法的StringBuilder,但没有合适的初始化大小。通过比较字段来执行.equals将会快得多。

编辑:

我得到了一个有趣的反射性能改进。我们现在使用反射来窥视AnnotationFactory类内部,以直接读取字段列表。这使得反射类的速度提高了20倍,因为它绕过了使用method.equals()调用的invoke。这不是一个通用的解决方案,但这里有来自ORMLite SVN repository的Java代码。有关通用解决方案,请参阅yanchenko's answer below

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-09-14 22:28:09

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

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

因此,至少他们知道它,并理应在未来的某个版本中修复它。

票数 22
EN

Stack Overflow用户

发布于 2013-08-06 03:39:30

以下是Gray's & user931366's想法的一个通用版本:

代码语言:javascript
复制
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次迭代,要么获取所有值,要么调用上面的方法:

代码语言:javascript
复制
     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

票数 6
EN

Stack Overflow用户

发布于 2012-09-13 04:37:08

为了跟进这一点,在注释上调用方法时仍然存在一个问题。上面由candrews列出的bug修复了getAnnotation()速度慢的问题,但由于Method.equals()问题,在注释上调用方法仍然是一个问题。

找不到Method.equals()的错误报告,所以我在这里创建了一个:https://code.google.com/p/android/issues/detail?id=37380

编辑:所以我在这方面的工作(感谢@Gray的想法),实际上非常简单。(这是主干代码,一些缓存等被省略了)

代码语言:javascript
复制
annotationFactory = Class.forName("org.apache.harmony.lang.annotation.AnnotationFactory");
getElementDesc = annotationFactory.getMethod("getElementsDescription", Class.class);
Object[] members = (Object[])getElementDesc.invoke(annotationFactory, clz); // these are AnnotationMember[]

Object element = null;
for (Object e:members){ // AnnotationMembers
    Field f = e.getClass().getDeclaredField("name");
    f.setAccessible(true);
    String fname = (String) f.get(e);
    if (methodName.equals(fname)){
        element = e;
    break;
    }
}

if (element == null) throw new Exception("Element was not found");
Method m = element.getClass().getMethod("validateValue");
return m.invoke(element, args);

您的里程数将根据使用情况而有所不同,但在5月份的情况下,这比使用“正确的方式”快15-20倍。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7417426

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档