Java, отражение, производительность и т.д.

Итак, я знаю, что эта тема была сделана раньше, например. Java Reflection Performance, но мой конкретный вопрос заключается в том, что многие популярные библиотеки реализованы с помощью аннотаций и отражений (например, Gson, Jackson, Jaxb, hibernate search), Многие (если не все) библиотеки обеспечивают хорошую (или большую) производительность, даже если они используют отражение. Мой вопрос: как они это делают? Есть ли какие-то "трюки", чтобы знать или они просто используют прямое отражение, а беспокойства в спектаклях раздуты?

EDIT: Так, например, когда мы пишем:   MyObject obj = new Gson(). FromJson (someInputStream, MyObject.class);

Я могу понять, как библиотека может внутренне кэшировать объекты Field, но мне кажется, что она должна постоянно отражать объект каждый раз, и ему нужно вызвать setter в каждом поле (отражательно) на основе проанализированного значения от json. Или есть какой-то способ заплатить (все) стоимость отражения только при запуске?

Я определенно заметил, что Gson/Jackson и т.д. имеют относительно большую начальную стоимость и после этого супер быстро. Поэтому, очевидно, мне интересно, если я напишу библиотеку, которая делает что-то смутно похожее, есть ли трюки, о которых мне нужно знать? Потому что, похоже, вы не можете уйти от некоторого количества отражения, при каждом вызове.

Ответ 1

Что дорого, но поиск метода один раз очень похож.

Итак, как только вы обнаружили, какой метод вызывать, вы просто держите ссылку на него, и последовательные вызовы работают аналогичным образом.

Конечно, есть ситуации, когда вы хотите сократить каждую миллисекунду.

Хотя вы должны beaware of micro benchmarks, вы можете попробовать это, чтобы получить приблизительную идею:

import java.lang.reflect.*;
class ReflectionOrNot { 
    public void run() { 
        try { 
            Thread.currentThread().sleep( 0 );
        } catch( InterruptedException ie ){}
    }

    public static void main( String ... args ) throws Exception { 

        ReflectionOrNot ron = new ReflectionOrNot();
        int max = 1000000;

        long start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
            ron.run();
        }
        System.out.println( "Direct access took: " + ( System.currentTimeMillis() - start ) );


        Method m = ReflectionOrNot.class.getDeclaredMethod("run");
        start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
            m.invoke( ron );
        }
        System.out.println( "Reflection    Took: " + ( System.currentTimeMillis() - start ) );


        start = System.currentTimeMillis();
        for( int i = 0 ; i < max ; i++ ) { 
             m = ReflectionOrNot.class.getDeclaredMethod("run");
            m.invoke( ron );
        }
        System.out.println( "Lookup + Reflect  : " + ( System.currentTimeMillis() - start ) );


    }
}

Вызов 1 миллион раз с разными подходами дал мне:

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 422
Reflection    Took: 1156
Lookup + Reflect  : 3016

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 422
Reflection    Took: 1125
Lookup + Reflect  : 2750

C:\Users\oreyes\java>java ReflectionOrNot
Direct access took: 485
Reflection    Took: 1203
Lookup + Reflect  : 2797

Ответ 2

Обычно нет трюков. Большинство действий, основанных на отражении, выполняются на application startup, тем самым не влияя на производительность во время выполнения после инициирования. Прекрасным примером этого является весь API аннотации гибернации.

Иногда присутствие аннотаций должно влиять на приложение на протяжении всего его жизненного цикла. Обычно это настраивается с помощью динамических прокси (или, cglib proxies при проксировании бетонных клад) или перехватчиков на основе исходных показаний отражения.

Ответ 3

Отражение медленное по сравнению с использованием данных, размещенных в приложении компилятором, но быстро по сравнению с предоставлением одних и тех же данных из базы данных.

Пока информация, которую приложение получает через отражение, извлекается по требованию и сохраняется в локальном кеше (или в виде инициализированных объектов), так что поиск является одноразовым событием в течение жизненного цикла приложения, которое вы не нужно беспокоиться о том, что отражение является узким местом производительности.

Ответ 4

Фокус в том, чтобы использовать отражение в "время конфигурации", а не "время выполнения".

Используйте отражение, чтобы проверить все необходимое, а затем сохраните (в памяти) эту информацию для использования во время выполнения.

Ответ 5

Только оригинальный поиск обширен, если у вас есть вся информация, которая вам нужна о классе и его методах, там не так много.

Так как бизнес-уровень должен работать долгое время, запуск немного медленнее, но после этого нет производительности.