这是我参与「第四届青训营 」笔记创作活动的的第1天
[第四届青训营笔记创作活动]
前言:什么是反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
(1)反射的结构API
(2)反射的使用方法
1、获取想要操作的Class对象,他是反射的核心,通过Class对象我们可以任意调用类的方法。
2、调用Class类中的方法,既是反射的使用阶段。
3、使用反射API来操作这些信息。
参考文章:
(3)反射性能影响的几个因素
1、反射获取类、方法和属性都需要通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化
2、反射类如果进行安全检查会消耗大量的资源
反射优化的2个思路:
1、使用方法前使用setAccessible() 方法关闭安全检查
2、把需要调用的方法、类、对象在使用前预先缓存、或者使用后缓存起来,减少每次调用时从jvm获取信息的时间
参考文章:
(4)几种反射调用方法的性能比较
测试代码:
mport com.esotericsoftware.reflectasm.MethodAccess;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethod {
//测试set、get方法
private static final TestBean bean = new TestBean();
private static final Long loop = 10 * 10000 * 10000l;
//reflectAsm缓存
private static final MethodAccess ma = MethodAccess.get(TestBean.class);
private static final int idx = ma.getIndex("setAge", Integer.class);
public static void ClassLoader() throws ClassNotFoundException {
long start = System.currentTimeMillis();
Class.forName("ReflectCache");
long used = System.currentTimeMillis() - start;
//System.out.println("反射加载消耗时间:" + used + " ms");
start = System.currentTimeMillis();
Class.forName("CglibFastCache");
used = System.currentTimeMillis() - start;
//System.out.println("fastMethod加载消耗时间:" + used + " ms");
start = System.currentTimeMillis();
MethodAccess.get(TestBean.class);
used = System.currentTimeMillis() - start;
//System.out.println("MethodAccess的get方法消耗时间:" + used + " ms");
}
public static void testNormal() {
//直接调用
long start = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
bean.setAge(i);
}
long used = System.currentTimeMillis() - start;
System.out.println("直接调用消耗时间:" + used + " ms");
}
public static void testReflect() {
//使用反射调用
long start = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
try {
ReflectCache.setAge.invoke(bean, i);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
long used = System.currentTimeMillis() - start;
System.out.println("反射调用消耗时间:" + used + " ms");
}
public static void testFastMethod() {
Integer[] input = new Integer[1];
//使用反射调用
long start = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
input[0] = i;
try {
CglibFastCache.fastSetAge.invoke(bean, input);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
long used = System.currentTimeMillis() - start;
System.out.println("fastMethod调用消耗时间:" + used + " ms");
}
public static void testReflectAsm() {
//使用reflectAsm
long start = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
ma.invoke(bean, idx, i);
}
long used = System.currentTimeMillis() - start;
System.out.println("reflectAsm调用消耗时间:" + used + " ms");
}
}
//反射缓存类
class ReflectCache {
static Class TestBeanClass;
static Method setAge;
static {
try {
TestBeanClass = Class.forName("TestBean");
setAge = TestBeanClass.getDeclaredMethod("setAge", Integer.class);
setAge.setAccessible(true);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
//Cglib的fastMethod
class CglibFastCache {
static FastClass fastTestBeanClass;
static FastMethod fastSetAge;
static {
try {
Class.forName("ReflectCache");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
fastTestBeanClass = FastClass.create(ReflectCache.TestBeanClass);
fastSetAge = fastTestBeanClass.getMethod(ReflectCache.setAge);
}
}
//测试类
class TestBean {
private String name;
private Integer age;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
}
测试结果:
这次只测试调用类的set方法,并且在调用前提前进行好类的获取,方法的获取等步骤,调用10亿次后的结果
| 调用方式 | 消耗时间(ms) | 比值(调用方法:直接调用) |
|---|---|---|
| 直接调用 | 2172 | 1 |
| fastMethod | 4077 | 1.88 |
| reflectAsm | 3483 | 1.6 |
| 反射优化 | 3740 | 1.72 |
总结:可能由于只测试了setAge()方法,在10亿次调用下,缓存了所需的类和所需的方法下,反射的时间大约在直接调用的时间的1.5~2倍,所以在方法不太复杂的情况下,反射的消耗主要是由于对类的获取和对方法的获取。
个人思考: 需要反射的类存储了大量的数据是否会对反射的性能造成影响?反射的方法逻辑复杂对性能的影响?反射的方法调用其他外部类或内部类的方法,对加载和调用的性能影响?