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 setDa(String da) {
			this.da = da;
		}

		public Integer getShao() {
			return shao;
		}

		public void setShao(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];
        setShao();
        return null;
_L3:
        (String)aobj[0];
        setDa();
        return null;
_L4:
        getDa();
        return;
_L5:
        getShao();
        return;
_L6:
        aobj[0];
        equals();
        JVM INSTR new #91  ;
        JVM INSTR dup_x1 ;
        JVM INSTR swap ;
        Boolean();
        return;
_L7:
        toString();
        return;
_L8:
        hashCode();
        JVM INSTR new #77  ;
        JVM INSTR dup_x1 ;
        JVM INSTR swap ;
        Integer();
        return;
        JVM INSTR new #73  ;
        JVM INSTR dup_x1 ;
        JVM INSTR swap ;
        InvocationTargetException();
        throw ;
_L1:
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

但FastClass是否真的能比反射调用速度快,以下用例用于验证:

package com.learn.cglib;

import java.lang.reflect.Method;

import org.junit.Test;

import net.sf.cglib.core.Constants;
import net.sf.cglib.reflect.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.setDa("duo");
		a.setShao(1);
		
		long start = System.nanoTime();
		FastClass fastA = FastClass.create(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
	 * 
	 * -Dsun.reflect.inflationThreshold=2147483647 耗时
	 * 2007548478
	 * 2283596315
	 * 2057156791
	 * 2109367771
	 * 2050146054
	 */
	@Test
	public void testReflex() throws Exception {
		A a = new A();
		a.setDa("duo");
		a.setShao(1);
		
		long start = System.nanoTime();
		Method getShao = A.class.getMethod("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 setDa(String da) {
			this.da = da;
		}

		public Integer getShao() {
			return shao;
		}

		public void setShao(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在执行反射时引入通胀概念,也即参数-Dsun.reflect.inflationThreshold(默认15)。当一段反射逻辑执行次数超过阈值后,会生成被反射类对应的字节码访问器(sun/reflect/GeneratedMethodAccessor和sun/reflect/DelegatingClassLoader),用于加快反射调用。测试代码中提高通胀阈值后发现反射的执行效率变得很差。

使用FastClass或反射生成字节码访问器都会构造新类占用perm区,网上看到一些oom的例子。但从执行效率方面考量,反射性能更好。

你可能感兴趣的:(java)