肥水管理
FastClass和Reflex性能⽐较
Cglib中提供FastClass增强功能,FastClass顾名思义是⼀个能让被增强类更快调⽤的Class,主要针对调⽤⽅法是变量的场景,⽤于替代反射调⽤。
FastClass的实现逻辑,是⽣成增强类实现invoke⽅法,invoke⽅法中,⽤switch语义将被增强类的所有⽅法调⽤枚举出来。⽤户使⽤FastClass.invoke⽅法,传⼊⽅法签名和被调⽤实例,从⽽达到不使⽤反射就能实现不确定⽅法的调⽤。
看⼀段invoke⽅法实现的例⼦,⼊参i是⽅法的index,由⽅法名和⼊参类型计算获得。
原⽅法:
public static class A {
private String da;
private Integer shao;
public String getDa() {
return da;
}
public void tDa(String da) {
this.da = da;
}
public Integer getShao() {
return shao;
}失眠的治疗方法
public void tShao(Integer shao) {
this.shao = shao;
}
}
FastClass增强类的invoke实现:
public Object invoke(int i, Object obj, Object aobj[])
throws InvocationTargetException
{
(FastClassTest.A)obj;
i;
JVM INSTR tableswitch 0 6: default 109
// 0 48
// 1 59
// 2 70
/
/ 3 74
// 4 78苏炳添事迹
// 5 93
// 6 97;
goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8
_L2:
(Integer)aobj[0];
tShao();
return null;
_L3:
(String)aobj[0];
tDa();
return null;
_L4:
getDa();
return;
_L5:
getShao();
return;
_L6:
aobj[0];
equals();
JVM INSTR new #91 <Class Boolean>;
JVM INSTR dup_x1 ;《西游记》好词
JVM INSTR swap ;
Boolean();
return;
_L7:
toString();
return;
_L8:
hashCode();
JVM INSTR new #77 <Class Integer>;
JVM INSTR dup_x1 ;
JVM INSTR swap ;
Integer();
return;
JVM INSTR new #73 <Class InvocationTargetException>;
JVM INSTR dup_x1 ;
JVM INSTR swap ;
InvocationTargetException();
throw ;
_L1:
throw new IllegalArgumentException("Cannot find matching method/constructor"); }
但FastClass是否真的能⽐反射调⽤速度快,以下⽤例⽤于验证:package lib;
水浒好汉import flect.Method;
import org.junit.Test;
import Constants;
import flect.FastClass;
/*
* fastClass将类⽅法⽤switch组织起来,从⽽将反射调⽤的需求变成常规调⽤。
* fastClass会⽣成新类,这是asm类框架的通病,⽣成太多会压垮perm。
*/
public class FastClassTest {
/*
* 耗时
* 312171361
* 344748612
* 318331937
* 327100471
* 316368564
*/
@Test
public void testFastClass() throws Exception {
A a = new A();
a.tDa("duo");
a.tShao(1);消防主题画
long start = System.nanoTime();
FastClass fastA = ate(A.class);
for (int i = 0; i < 10000000; i++) {
fastA.invoke("getShao", Constants.EMPTY_CLASS_ARRAY, a, new Object[] {}); }
long end = System.nanoTime();
System.out.println(end - start);
}
/*
* 耗时
* 64352651
* 61022533
* 62674898
* 96095258
* 71985404
*
* -flect.inflationThreshold=2147483647 耗时
* 2007548478
* 2283596315
* 2057156791
* 2109367771
* 2050146054
*/
@Test
public void testReflex() throws Exception {
赏心悦目的意思是
A a = new A();
a.tDa("duo");
a.tShao(1);
long start = System.nanoTime();
Method getShao = Method("getShao");
for (int i = 0; i < 10000000; i++) {
getShao.invoke(a);
}
long end = System.nanoTime();
System.out.println(end - start);
}
public static class A {
private String da;
private Integer shao;
public String getDa() {
return da;
}
public void tDa(String da) {
this.da = da;
}
public Integer getShao() {
return shao;
}
public void tShao(Integer shao) {
this.shao = shao;
}
}
}
测试环境:
cpu:Inter(R) Core(TM) i7-7500U CPU @ 2.70GHZ 2.90GHZ
jre:1.8.0_131
junit:4.10
鸭子英语
从测试结果上看,⼀千万次调⽤的总耗时,默认情况下,反射执⾏效率明显优于FastClass。反射操作的时间开销主要存在于获取被反射的⽅法信息上,默认情况下使⽤JNI访问器获得该信息。但⾼版本的JVM在执⾏反射时引⼊通胀概念,也即参数-
(sun/reflect/GeneratedMethodAccessor和sun/reflect/DelegatingClassLoader),⽤于加快反射调⽤。测试代码中提⾼通胀阈值后发现反射的执⾏效率变得很差。
使⽤FastClass或反射⽣成字节码访问器都会构造新类占⽤perm区,⽹上看到⼀些oom的例⼦。但从执⾏效率⽅⾯考量,反射性能更好。