In depth analysis of java reflection (8) – optimize reflection call performance

The java reflection API is in javase 1 7, but Oracle jdk11 was used when writing this article, because jdk11 also uploaded the source code under the sun package. You can directly view the corresponding source code and debug through the IDE.

The previous article has introduced the underlying principle of reflection call. In fact, in practice, for most Java users, it is more related to how to improve the performance of reflection call. This paper mainly provides several feasible schemes. In addition, as the reflection operation with the highest frequency during method call, we will focus on the reflection call optimization of methods.

Method 1: select the appropriate API

Selecting an appropriate API is mainly to avoid traversal when obtaining reflection related metadata, for example:

In fact, the idea is very simple. Unless we want to obtain all fields, methods or constructors of class, we should avoid using the API that returns a set or array, so as to reduce the performance loss caused by traversal or judgment.

Method 2: cache metadata related to reflection operation

The reason why the caching mechanism is used to cache the metadata related to reflection operations is that the real-time acquisition of metadata related to reflection operations is time-consuming. Here are several relatively time-consuming scenarios:

Here is a simple example. You need to call the setter and getter methods of an ordinary JavaBean by reflection:

// JavaBean
@Data
public class JavaBean {

    private String name;
}

public class Main {

	private static final Map<Class<?>,List<ReflectionMetadata>> MetaDATA = new HashMap<>();
	private static final Map<String,Class<?>> CLASSES = new HashMap<>();

	// 解析的时候尽量放在<cinit>里面
	static {
		Class<?> clazz = JavaBean.class;
		CLASSES.put(clazz.getName(),clazz);
		List<ReflectionMetadata> MetadataList = new ArrayList<>();
		MetaDATA.put(clazz,MetadataList);
		try {
			for (Field f : clazz.getDeclaredFields()) {
				ReflectionMetadata Metadata = new ReflectionMetadata();
				MetadataList.add(Metadata);
				Metadata.setTargetClass(clazz);
				Metadata.setField(f);
				String name = f.getName();
				Class<?> type = f.getType();
				Metadata.setReadMethod(clazz.getDeclaredMethod(String.format("get%s%s",Character.toUpperCase(name.charAt(0)),name.substring(1))));
				Metadata.setWriteMethod(clazz.getDeclaredMethod(String.format("set%s%s",name.substring(1)),type));
			}
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public static void main(String[] args) throws Exception {
		String fieldName = "name";
		Class<JavaBean> javaBeanClass = JavaBean.class;
		JavaBean javaBean = new JavaBean();
		invokeSetter(javaBeanClass,javaBean,fieldName,"Doge");
		System.out.println(invokeGetter(javaBeanClass,fieldName));
		invokeSetter(javaBeanClass.getName(),"Throwable");
		System.out.println(invokeGetter(javaBeanClass.getName(),fieldName));
	}

	private static void invokeSetter(String className,Object target,String fieldName,Object value) throws Exception {
		MetaDATA.get(CLASSES.get(className)).forEach(each -> {
			Field field = each.getField();
			if (field.getName().equals(fieldName)) {
				try {
					each.getWriteMethod().invoke(target,value);
				} catch (Exception e) {
					throw new IllegalStateException(e);
				}
			}
		});
	}

	private static void invokeSetter(Class<?> clazz,Object value) throws Exception {
		MetaDATA.get(clazz).forEach(each -> {
			Field field = each.getField();
			if (field.getName().equals(fieldName)) {
				try {
					each.getWriteMethod().invoke(target,value);
				} catch (Exception e) {
					throw new IllegalStateException(e);
				}
			}
		});
	}

	private static Object invokeGetter(String className,String fieldName) throws Exception {
		for (ReflectionMetadata Metadata : MetaDATA.get(CLASSES.get(className))) {
			if (Metadata.getField().getName().equals(fieldName)) {
				return Metadata.getReadMethod().invoke(target);
			}
		}
		throw new IllegalStateException();
	}

	private static Object invokeGetter(Class<?> clazz,String fieldName) throws Exception {
		for (ReflectionMetadata Metadata : MetaDATA.get(clazz)) {
			if (Metadata.getField().getName().equals(fieldName)) {
				return Metadata.getReadMethod().invoke(target);
			}
		}
		throw new IllegalStateException();
	}

	@Data
	private static class ReflectionMetadata {

		private Class<?> targetClass;
		private Field field;
		private Method readMethod;
		private Method writeMethod;
	}
}

In short, parsing reflection metadata for caching is best placed in static code blocks or the first call (that is, lazy loading), so as to avoid the need to reload reflection related metadata in the real call.

Method 3: convert reflection operation to direct call

"Turning reflection operations into direct calls" is not a class library that does not depend on reflection at all. The approach here is to place the metadata related to reflection operations directly in the member variables of the class, so as to save the consumption of reading reflection related metadata from the cache. The so-called "direct calls" are generally implemented through inheritance or implementation interfaces. Some high-performance reflection class libraries also use some innovative methods: for example, use the member attribute cache to reflect relevant metadata, and use the method call to establish an index [number - > method] or an index class (such as fastclass of cglib). This method will improve the performance when there are few parent classes or interface methods, However, in fact, performance evaluation needs to analyze the results from specific scenarios, and can not be used blindly. The class libraries using this idea include cglib, reflectasm, etc. The most typical implementation of "turning reflection operation into direct call" is the dynamic proxy of JDK. Here is an example from the previous article on dynamic proxy:

// 接口
public interface Simple {

    void sayHello(String name);
}
// 接口实现
public class DefaultSimple implements Simple {

    @Override
    public void sayHello(String name) {
        System.out.println(String.format("%s say hello!",name));
    }
}
// 场景类
public class Main {

    public static void main(String[] args) throws Exception {
        Simple simple = new DefaultSimple();
        Object target = Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{Simple.class},new InvocationHandler() {
            @Override
            public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
                System.out.println("Before say hello...");
                method.invoke(simple,args);
                System.out.println("After say hello...");
                return null;
            }
        });
        Simple proxy = (Simple) target;
        proxy.sayHello("throwable");
    }
}

// 代理类
public final class $Proxy0 extends Proxy implements Simple {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this,m1,new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello(String var1) throws  {
        try {
            super.h.invoke(this,m3,new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this,m2,(Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this,m0,(Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getmethod("equals",Class.forName("java.lang.Object"));
            m3 = Class.forName("club.throwable.jdk.sample.reflection.proxy.Simple").getmethod("sayHello",Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getmethod("toString");
            m0 = Class.forName("java.lang.Object").getmethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

In this way, although the simple interface instance finally calls the sayhello (string VAR1) method through reflection, but the relevant metadata is created in the static code block and cached in the class member attribute, the performance of the reflection calling method has been optimized to the extreme, and the rest is only the time-consuming of the native method, which the user has no way to optimize at the coding level, Reflection performance can only be improved by upgrading JVM (JDK), using JIT compiler and other non coding methods.

Summary

This paper mainly analyzes some feasible experience or schemes for performance optimization of reflection operation from the coding level. There may be other better optimization schemes. The specific situation still depends on the use scenario.

Personal blog

(end of this paper e-a-20181216 c-2-d)

The official account of Technology (Throwable Digest), which is not regularly pushed to the original technical article (never copied or copied):

Entertainment official account ("sand sculpture"), select interesting sand sculptures, videos and videos, push them to relieve life and work stress.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>