предисловие
В нашей обычной работе или собеседованиях мы часто сталкиваемся с точкой познания «отражение».Благодаря «отражению» мы можем динамически получать информацию об объекте и гибко вызывать метод объекта и т. д., но в то же время, он сопровождается другим звуком. Один из видов звука, который появляется, говорит о том, что «отражение» происходит очень медленно и его следует использовать с осторожностью. Действительно ли отражения медленные? Насколько это медленнее, чем когда мы обычно создаем объект и вызываем метод?По оценкам, многие люди не проверяли его, просто «по слухам». Далее мы непосредственно испытаем «отражение» через несколько тестовых случаев.
текст
Подготовьте испытуемых
Давайте сначала определим тестовый классTestUser,Толькоidиnameсвойства и их методы получения/установки, а также настраиваемыйsayHiметод.
public class TestUser {
private Integer id;
private String name;
public String sayHi(){
return "hi";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Протестируйте создание 1 миллиона объектов
// 通过普通方式创建TestUser对象
@Test
public void testCommon(){
long start = System.currentTimeMillis();
TestUser user = null;
int i = 0;
while(i<1000000){
++i;
user = new TestUser();
}
long end = System.currentTimeMillis();
System.out.println("普通对象创建耗时:"+(end - start ) + "ms");
}
//普通对象创建耗时:10ms
// 通过反射方式创建TestUser对象
@Test
public void testReflexNoCache() throws Exception {
long start = System.currentTimeMillis();
TestUser user = null;
int i = 0;
while(i<1000000){
++i;
user = (TestUser) Class.forName("ReflexDemo.TestUser").newInstance();
}
long end = System.currentTimeMillis();
System.out.println("无缓存反射创建对象耗时:"+(end - start ) + "ms");
}
//无缓存反射创建对象耗时:926ms
В приведенных выше двух методах тестирования автор протестировал каждый из них 5 раз и взял среднее значение их потребления времени.В результатах вывода мы видим, что один составляет 10 мс, а другой - 926 мс.В случае создания 100 Вт объекты вниз, отражение на самом деле примерно в 90 раз медленнее. что? Так ли велика разница? Действительно ли отражение настолько медленное? Затем я меняю рефлекторную позу, продолжаю тестировать и смотрю, каков результат?
// 通过缓存反射方式创建TestUser对象
@Test
public void testReflexWithCache() throws Exception {
long start = System.currentTimeMillis();
TestUser user = null;
Class rUserClass = Class.forName("RefleDemo.TestUser");
int i = 0;
while(i<1000000){
++i;
user = (TestUser) rUserClass.newInstance();
}
long end = System.currentTimeMillis();
System.out.println("通过缓存反射创建对象耗时:"+(end - start ) + "ms");
}
//通过缓存反射创建对象耗时:41ms
Хм? Эта операция занимает всего 41 мс, что значительно повышает эффективность создания объектов путем отражения. Почему так быстрее?
На самом деле из кода видно, что метод Class.forName более затратный по времени, он фактически вызывает локальный метод, через который JVM требуется найти и загрузить указанный класс. Поэтому, когда мы используем его в проекте, мы можем кэшировать объект Class, возвращаемый Class.forName, и получать его прямо из кеша при следующем использовании, что значительно повышает эффективность получения Class. Точно так же, когда мы получаем конструктор, метод и другие объекты, мы также можем кэшировать их и использовать, чтобы избежать трудоемкого создания каждый раз, когда мы их используем.
Протестируйте метод вызова отражения
@Test
public void testReflexMethod() throws Exception {
long start = System.currentTimeMillis();
Class testUserClass = Class.forName("RefleDemo.TestUser");
TestUser testUser = (TestUser) testUserClass.newInstance();
Method method = testUserClass.getMethod("sayHi");
int i = 0;
while(i<100000000){
++i;
method.invoke(testUser);
}
long end = System.currentTimeMillis();
System.out.println("反射调用方法耗时:"+(end - start ) + "ms");
}
//反射调用方法耗时:330ms
@Test
public void testReflexMethod() throws Exception {
long start = System.currentTimeMillis();
Class testUserClass = Class.forName("RefleDemo.TestUser");
TestUser testUser = (TestUser) testUserClass.newInstance();
Method method = testUserClass.getMethod("sayHi");
int i = 0;
while(i<100000000){
++i;
method.setAccessible(true);
method.invoke(testUser);
}
long end = System.currentTimeMillis();
System.out.println("setAccessible=true 反射调用方法耗时:"+(end - start ) + "ms");
}
//setAccessible=true 反射调用方法耗时:188ms
Здесь мы вызываем метод sayHi 100 миллионов раз путем отражения, после вызова method.setAccessible(true) выясняется, что он почти вдвое быстрее. Глядя на API видно, что jdk будет выполнять проверки доступа к безопасности при настройке полей сбора и вызове методов, и такие операции занимают много времени, поэтому проверки безопасности можно отключить с помощью setAccessible(true), тем самым улучшив отражение эффективность.
окончательное отражение
В дополнение к вышеупомянутым методам, есть ли способ использовать более экстремальное отражение? Вот высокопроизводительный набор инструментов для отражения ReflectASM. Это механизм отражения, реализованный путем генерации байт-кода.Ниже приведено сравнение производительности с отражением java.
Я не буду вводить его использование здесь, и заинтересованные друзья могут отправить его напрямую:GitHub.com/esoteric, так что F…
Эпилог
Наконец, чтобы подвести итог, чтобы лучше использовать отражение, мы должны загружать соответствующую конфигурацию и данные, необходимые для отражения, в память при запуске проекта и извлекать эти метаданные из кеша для операций отражения на этапе выполнения. Вам не нужно бояться отражения. Виртуальная машина постоянно оптимизируется. Пока мы используем правильный метод, она не такая медленная, как «слухи». Когда у нас есть предельная погоня за производительностью, мы можем рассмотрите возможность использования стороннего пакета для прямого сопоставления слов с кодом раздела для работы.
Сообщение в блоге публичного аккаунта синхронизировано с репозиторием Github. Заинтересованные друзья могут помочь дать звезду. Кодировать слова непросто. Спасибо за вашу поддержку.
Рекомендуемое чтение
"Правильное использование позы для журналов Java》
"Является ли использование ConcurrentHashMap определенным потокобезопасным?》
"Народный язык понимает, что такое синхронный/асинхронный/блокирующий/неблокирующий》
"О нескольких позах и способах самоспасения при взрыве JVM》
Обратите внимание на «Программу обезьяны посреди ночи» и поделитесь лучшими галантерейными товарами