Я ведущий автор ORMLite, который использует аннотации Java для классов для построения схем баз данных. Большая проблема производительности стартового пакета для нашего пакета оказывается вызовом методов аннотации в Android 1.6. Я вижу то же поведение до 3,0.
Мы видим, что следующий простой код аннотации невероятно насыщен GC и реальная проблема с производительностью. 1000 вызовов метода аннотации занимают почти секунду на быстром Android-устройстве. Тот же код, который работает на моем Macbook Pro, может выполнять 28 миллионов (sic) звонков в одно и то же время. У нас есть аннотация, в которой есть 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
EDIT:
После того, как @candrews указал мне в правильном направлении, я немного проговорил над кодом. Проблема производительности связана с каким-то ужасным, грубым кодом в Method.equals()
. Он вызывает toString()
обоих методов, а затем сравнивает их. Каждый toString()
использует StringBuilder
с кучей методов добавления без хорошего размера инициализации. Выполнение .equals
путем сравнения полей будет значительно быстрее.
EDIT:
Мне понравилось интересное улучшение производительности. Мы теперь используем рефлексию, чтобы заглянуть внутрь класса AnnotationFactory
, чтобы прочитать список полей напрямую. Это делает класс отражения в 20 раз быстрее для нас, поскольку он обходит вызов, который использует вызов Method.equals()
. Это не общее решение, а вот код Java из ORMLite SVN репозиторий. Для общего решения см. ответ Янченко ниже.