Java 中反射、內(nèi)省的性能差距居然如此
你好,我是看山。
今天我們通過基準(zhǔn)測試驗(yàn)證下到底有多慢。
結(jié)果是我萬萬沒想到的,跑了好幾遍基準(zhǔn)測試,不得不承認(rèn)之前是自己不嚴(yán)謹(jǐn)了。
演示代碼
先定義使用場景:為一個(gè)JavaBean的屬性賦值。
@Data
public static class User {
private String username;
private int level;
}
為User的屬性賦值,我們有三種方式。
原生方式,作為基準(zhǔn):
final User user = new User();
user.setUsername("看山");
user.setLevel(100);
通過反射:
final Class<User> clazz = User.class;
final Constructor<User> constructor = clazz.getConstructor();
final User user = constructor.newInstance();
final Field usernameField = clazz.getDeclaredField("username");
usernameField.setAccessible(true);
usernameField.set(user, "看山");
final Field levelField = clazz.getDeclaredField("level");
levelField.setAccessible(true);
levelField.set(user, 100);
反射也可以通過調(diào)用setter方法賦值:
final Class<User> clazz = User.class;
final Constructor<User> constructor = clazz.getConstructor();
final User user = constructor.newInstance();
final Method setUsernameMethod = clazz.getMethod("setUsername", String.class);
setUsernameMethod.invoke(user, "看山");
final Method setLevelMethod = clazz.getMethod("setLevel", int.class);
setLevelMethod.invoke(user, 100);
使用內(nèi)省賦值:
final BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
final User user = new User();
final PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
if ("username".equals(prop.getName())) {
final Method method = prop.getWriteMethod();
method.invoke(user, "看山");
} else if ("level".equals(prop.getName())) {
final Method method = prop.getWriteMethod();
method.invoke(user, 100);
}
}
內(nèi)省方式也可以直接指定屬性:
final User user = new User();
final PropertyDescriptor usernameProp = new PropertyDescriptor("username", User.class);
final Method usernameWriteMethod = usernameProp.getWriteMethod();
usernameWriteMethod.invoke(user, "看山");
final PropertyDescriptor levelProp = new PropertyDescriptor("level", User.class);
final Method levelWriteMethod = levelProp.getWriteMethod();
levelWriteMethod.invoke(user, 100);
好了,基準(zhǔn)測試的幾種情況已經(jīng)準(zhǔn)備好了,為了達(dá)到更充分的驗(yàn)證,我們分別循環(huán)執(zhí)行10、100、200 、500次,我們跑一下基準(zhǔn)測試看看效果。
測試效果
保留下500次循環(huán)的數(shù)據(jù)(回復(fù):Java可以獲取源碼)
Benchmark Score Error
BeanSetJmhTest.testBase 671.299 ± 14.201 基準(zhǔn)
BeanSetJmhTest.testAccessFieldCacheByReflectField 6451.184 ± 212.541 反射-緩存-屬性賦值
BeanSetJmhTest.testMethodCacheByReflect 13381.968 ± 1921.017 反射-緩存-方法賦值
BeanSetJmhTest.testMethodCacheByIntrospector 13523.807 ± 2146.288 內(nèi)省-緩存-方法賦值
BeanSetJmhTest.testMethodByReflect 44874.497 ± 14215.009 反射-方法賦值
BeanSetJmhTest.testAccessFieldByReflect 57989.549 ± 282731.822 反射-屬性賦值
BeanSetJmhTest.testAccessFieldCacheByIntrospectorDirectProp 121879.007 ± 28027.596 內(nèi)省-緩存-指定屬性賦值
BeanSetJmhTest.testAccessFieldCacheByIntrospectorProps 167602.264 ± 30272.412 內(nèi)省-緩存-屬性循環(huán)賦值
BeanSetJmhTest.testAccessFieldByIntrospectorProps 204765.110 ± 53973.520 內(nèi)省-屬性循環(huán)賦值
BeanSetJmhTest.testAccessFieldByIntrospectorDirectProp 783250.528 ± 40212.597 內(nèi)省-指定屬性賦值
可視化結(jié)果:
基準(zhǔn)測試結(jié)果
從結(jié)果看:
- 在設(shè)置屬性方面,反射性能優(yōu)于內(nèi)??;【上面結(jié)果是在JDK21測試的,試過JDK8、JDK17結(jié)果相似,不太確定有些文章說的內(nèi)省性能優(yōu)于反射是怎么測試的?!浚?/li>
- 有緩存的邏輯性能會明顯優(yōu)于沒有緩存的邏輯,無論是反射還是內(nèi)省;
- 非必要情況,不要使用反射和內(nèi)省,直接用JavaBean的setter賦值,性能差的太多。