Comments 14
Есть еще один способ, не волшебный, правда, и не всегда применимый, но мега-крутой - compile-time кодогенерация и post-compile weaving. Последний вообще позволяет получить перфоманс "как будто бы сам написал".
Погрузится в пучины LambdaMetafactory можно тут https://github.com/FasterXML/jackson-modules-base/tree/master/blackbird
Заодно переедете с устаревшего Afterburner на новый Blackbird.
Спасибо за статью. Почему Вы не используете фреймворк для микробенчмаркинга (например, jmh)? Кроме того в 18ой джаве core reflections переписали на var handles.
https://openjdk.java.net/jeps/416
А кеширование как вариант почему не рассматривается?
Каким образом кэширование поможет ускорить рефлективные вызовы метода? Имхо, максимум можно кэшировать получение объекта java.lang.reflect.Method, но это даст выигрыш лишь в скорости подготовки вызова.
Если вы говорите о кэшировании сгенерированных прокси-классов, чтобы не создавать их заново для одной и той же связки класс-метод, то это уже по сути детали конкретной реализации, которые были опущены, чтобы не загружать обзорную статью.
P. S. Если имеется ввиду кэширование получаемой из метода информации, дабы предотвратить излишние вызовы, то это материал для совершенно другой статьи.
Ну нет же, JNI тут ни при чем. Современные JVM содержат интринсики для методов рефлексии. Например, вот так обрабатываются методы типа java.lang.Class.isInterface:
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/library_call.cpp#L3565
Ну смотрите. Байт-код можно либо интепретировать, либо компилировать в платформенный код. В обоих случаях мы можем вместо какого-то метода JVM вызвать просто какую-нибудь функцию самой JVM, чем и являются интринсики.
Ну а JNI - это сложнейший комбайн, который поддерживает потокобезопасность, открывает во внешний мир API JVM и делает еще кучу вещей, необходимых для стабильного FFI.
Рефлексия в Джаве помимо пользы может нести в себе кучу багов, которые потом хрен поймаешь. А так же это - отличный инструмент в руках хакера/злоумышленника. Получить доступ можно к чему угодно.
Занимательные замеры времени. Вывод же несколько ограниченный. Вся разница получается за счёт компиляции метода add в одну asm инструкцию вместо нескольких десятков для reflection-а. Ну а промежуточные значения есть лишь показатель способности методом тыка подсказать компилятору, как же правильно компилировать.
Ну и про "тяжесть" рефлексии. Просто не надо писать критический код с её использованием. И в подавляющем большинстве случаев для этого нет никакой необходимости. А некритический код, исполняемый за 1 микросекунду или за 10 наносекунд - абсолютно ни на что не влияет.
Ускоряем java-рефлексию в 2022