Java开发中那些可以提升性能/效率的小技巧(持续更新)
一、Java基础
1、使用反射时做好缓存
日常开发中,经常会用到反射,尤其是SpringAOP或者其他场景,但是经常会忽略其性能,反射的性能确实比普通的方法性能差很多。
下面是我们日常开发时使用反射的示例,100W次执行需要280毫秒:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;public class Test {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Test test = new Test();Date start = new Date();for (int i = 0; i < 1000000; i++) {Method test1 = Test.class.getDeclaredMethod("test1", String.class);Object invoke = test1.invoke(test, "123");}System.out.println(new Date().getTime() - start.getTime()); // 280}public void test1(String param) {int i = 0;i ++;}
}
加上Map缓存,我们查看一下性能:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class Test {private static Map<String, Method> map = new ConcurrentHashMap<>();public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Test test = new Test();Date start = new Date();for (int i = 0; i < 1000000; i++) {// Map缓存Method test1 = map.computeIfAbsent(Test.class.getName() + "test1", k -> {try {return Test.class.getDeclaredMethod("test1", String.class);} catch (NoSuchMethodException e) {e.printStackTrace();}return null;});Object invoke = test1.invoke(test, "123");}System.out.println(new Date().getTime() - start.getTime()); // 140}public void test1(String param) {int i = 0;i ++;}
}
前后经过对比,280ms和140ms,能差一倍的性能!
但是,这是100万次反射获取方法,280毫秒其实对于一般系统来说也影响不大了,如果是追求极致的并发,或许可以考虑一下为反射添加缓存。
2、public类型的Method尽量也setAccessible(true)
setAccessible可以将方法、字段的检查设置为true(跳过检查
),默认是false(需要进行检查,既private、protected等检查)。
相当于将java.lang.reflect.AccessibleObject中override
字段设置为true了:
如果通过反射调用方法时,需要进行override字段的校验,而这段代码中包含了不少的反射逻辑及native方法调用:
日常开发中,以及某些Spring的代码中,对于字段或者方法设置Access时,public类型的就不进行setAccessible(true)
的操作了,当然这对于最终的结果并没有影响,但是最好是全都进行setAccessible(true)
的操作,这会跳过一大段检查的逻辑:
org.springframework.util.ReflectionUtils#makeAccessible(java.lang.reflect.Constructor<?>)
:
建议去掉判断,直接进行setAccessible(true)
的操作,不管类是否是public的、不管方法和字段是public的。
3、不要在方法级别使用Class.forName
调用Class.forName
方法时,会执行Classloader的loadClass方法,这个方法是带着synchronized的
!
所以,在并发情况下,会有锁竞争的场景,比如说Spring中的依赖注入,为了检查Inject类是否存在,在构造方法中做了检查。当然这里也不会有什么并发问题,该构造方法也只会调用一次。
正确的方法是在static块中进行调用: