Java 中反射、內省的性能差距居然如此
作者:看山灬
基準測試的幾種情況已經準備好了,為了達到更充分的驗證,我們分別循環執行10、100、200、500次,我們跑一下基準測試看看效果。
你好,我是看山。
今天我們通過基準測試驗證下到底有多慢。
結果是我萬萬沒想到的,跑了好幾遍基準測試,不得不承認之前是自己不嚴謹了。
演示代碼
先定義使用場景:為一個JavaBean的屬性賦值。
@Data
public static class User {
private String username;
private int level;
}
為User的屬性賦值,我們有三種方式。
原生方式,作為基準:
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);
反射也可以通過調用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);
使用內省賦值:
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);
}
}
內省方式也可以直接指定屬性:
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);
好了,基準測試的幾種情況已經準備好了,為了達到更充分的驗證,我們分別循環執行10、100、200 、500次,我們跑一下基準測試看看效果。
測試效果
保留下500次循環的數據(回復:Java可以獲取源碼)
Benchmark Score Error
BeanSetJmhTest.testBase 671.299 ± 14.201 基準
BeanSetJmhTest.testAccessFieldCacheByReflectField 6451.184 ± 212.541 反射-緩存-屬性賦值
BeanSetJmhTest.testMethodCacheByReflect 13381.968 ± 1921.017 反射-緩存-方法賦值
BeanSetJmhTest.testMethodCacheByIntrospector 13523.807 ± 2146.288 內省-緩存-方法賦值
BeanSetJmhTest.testMethodByReflect 44874.497 ± 14215.009 反射-方法賦值
BeanSetJmhTest.testAccessFieldByReflect 57989.549 ± 282731.822 反射-屬性賦值
BeanSetJmhTest.testAccessFieldCacheByIntrospectorDirectProp 121879.007 ± 28027.596 內省-緩存-指定屬性賦值
BeanSetJmhTest.testAccessFieldCacheByIntrospectorProps 167602.264 ± 30272.412 內省-緩存-屬性循環賦值
BeanSetJmhTest.testAccessFieldByIntrospectorProps 204765.110 ± 53973.520 內省-屬性循環賦值
BeanSetJmhTest.testAccessFieldByIntrospectorDirectProp 783250.528 ± 40212.597 內省-指定屬性賦值
可視化結果:
基準測試結果
從結果看:
- 在設置屬性方面,反射性能優于內省;【上面結果是在JDK21測試的,試過JDK8、JDK17結果相似,不太確定有些文章說的內省性能優于反射是怎么測試的。】;
- 有緩存的邏輯性能會明顯優于沒有緩存的邏輯,無論是反射還是內省;
- 非必要情況,不要使用反射和內省,直接用JavaBean的setter賦值,性能差的太多。
責任編輯:武曉燕
來源:
看山的小屋