Java如何提高反射性能

前言

我们都知道,通过类名反射创建实例的性能很差。

User user=Class.forName("com.xxx.xxx.User").newInstance();

那么如何提高创建对象的性能呢?

反射性能到底如何?

使用new创建对象和反射到底差距有多大?
下面编辑一段代码,进行10轮,每轮分别用new和反射创建10_000_000个User对象。

 @Test
    public void testObj() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        int n = 10_000_000;
        for (int k = 0; k < 10; k++) {
            System.out.println("---------" + k + "----------");

            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user = new User();
                }
                long e = System.currentTimeMillis();
                System.out.println("创建对象数量:" + n + ",new 对象的耗时: " + (e - s) + " 毫秒");
            }
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user = (User) Class.forName("com.it2.springbootredisson.pojo.User").newInstance();
                }
                long e = System.currentTimeMillis();
                System.out.println("创建对象数量:" + n + ",反射 对象的耗时: " + (e - s) + " 毫秒");
            }
        }
    }

运行结果

---------0----------
创建对象数量:10000000,new 对象的耗时: 39 毫秒
创建对象数量:10000000,反射 对象的耗时: 12788 毫秒
---------1----------
创建对象数量:10000000,new 对象的耗时: 142 毫秒
创建对象数量:10000000,反射 对象的耗时: 16379 毫秒
---------2----------
创建对象数量:10000000,new 对象的耗时: 5 毫秒
创建对象数量:10000000,反射 对象的耗时: 13758 毫秒
---------3----------
创建对象数量:10000000,new 对象的耗时: 5 毫秒
创建对象数量:10000000,反射 对象的耗时: 12010 毫秒
---------4----------
创建对象数量:10000000,new 对象的耗时: 5 毫秒
创建对象数量:10000000,反射 对象的耗时: 13448 毫秒
---------5----------
创建对象数量:10000000,new 对象的耗时: 5 毫秒
创建对象数量:10000000,反射 对象的耗时: 11192 毫秒
---------6----------
创建对象数量:10000000,new 对象的耗时: 4 毫秒
创建对象数量:10000000,反射 对象的耗时: 10540 毫秒
---------7----------
创建对象数量:10000000,new 对象的耗时: 4 毫秒
创建对象数量:10000000,反射 对象的耗时: 10708 毫秒
---------8----------
创建对象数量:10000000,new 对象的耗时: 7 毫秒
创建对象数量:10000000,反射 对象的耗时: 10142 毫秒
---------9----------
创建对象数量:10000000,new 对象的耗时: 5 毫秒
创建对象数量:10000000,反射 对象的耗时: 10275 毫秒

通过上面的对比试验,可以看到反射创建对象效率非常低。
但是有时候我们不得不通过className去创建对象,显然如果选择使用Class.forName(className).newInstance()去创建对象非常缓慢,怎么样才能提高呢?


创建对象的5种办法

这里找到了5个创建对象的方法

  • new 创建对象

我们希望通过className或者对象的Class来创建对象,这里就直接忽略这个办法了。

User user=new User();
  • deepCopy 通过字节流复制
 public static Object deepCopy(Object obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
  • org.apache.commons.beanutils.BeanUtils.cloneBean
User user = (User) org.apache.commons.beanutils.BeanUtils.cloneBean(user);
  • 反射1 直接使用Class.forName().newInstance()
User user=Class.forName("com.xxx.xxx.User").newInstance();
  • 反射2 Class.newInstance()
User user=clazz.newInstance();
  • org.springframework.beans.BeanUtils.instantiateClass
User user = org.springframework.beans.BeanUtils.instantiateClass(User.class);

通过上面4种方法,我们需要先测试一些性能。

编写测试用例,对上面5种方式做性能测试

 @Test
    public void testCreateObjectPerformance() throws Exception {
        Thread.sleep(1000);
        Class<?> clazz= Class.forName("com.it2.springbootredisson.pojo.User");

        for (int k = 0; k < 10; k++) {
            System.out.println("--------" + (k) + "----------");
            User user = new User();
            user.setId(122);
            int n = 1_000_000;
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user4 = (User) ObjectUtil.deepCopy(user);
                    user4.setId(333);
                }
                long e = System.currentTimeMillis();
                System.out.println("deepCopy产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user4 = (User) org.apache.commons.beanutils.BeanUtils.cloneBean(user);
                    user4.setId(333);
                }
                long e = System.currentTimeMillis();
                System.out.println("cloneBean产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user3 = (User) Class.forName("com.it2.springbootredisson.pojo.User").newInstance();
                    user3.setId(111);
                }
                long e = System.currentTimeMillis();
                System.out.println("反射1产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user3 = (User) clazz.newInstance();
                    user3.setId(111);
                }
                long e = System.currentTimeMillis();
                System.out.println("反射2产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }
            {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user3 = org.springframework.beans.BeanUtils.instantiateClass(User.class);
                    user3.setId(222);
                }
                long e = System.currentTimeMillis();
                System.out.println("instantiateClass产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }
        }
    }

运行结果

--------0----------
deepCopy产生 1000000 个User对象,耗时: 9085毫秒
cloneBean产生 1000000 个User对象,耗时: 2313毫秒
反射1产生 1000000 个User对象,耗时: 1248毫秒
反射2产生 1000000 个User对象,耗时: 33毫秒
instantiateClass产生 1000000 个User对象,耗时: 195毫秒
--------1----------
deepCopy产生 1000000 个User对象,耗时: 8031毫秒
cloneBean产生 1000000 个User对象,耗时: 1849毫秒
反射1产生 1000000 个User对象,耗时: 1045毫秒
反射2产生 1000000 个User对象,耗时: 13毫秒
instantiateClass产生 1000000 个User对象,耗时: 54毫秒
--------2----------
deepCopy产生 1000000 个User对象,耗时: 6758毫秒
cloneBean产生 1000000 个User对象,耗时: 1735毫秒
反射1产生 1000000 个User对象,耗时: 980毫秒
反射2产生 1000000 个User对象,耗时: 17毫秒
instantiateClass产生 1000000 个User对象,耗时: 62毫秒
--------3----------
deepCopy产生 1000000 个User对象,耗时: 6334毫秒
cloneBean产生 1000000 个User对象,耗时: 2094毫秒
反射1产生 1000000 个User对象,耗时: 1255毫秒
反射2产生 1000000 个User对象,耗时: 15毫秒
instantiateClass产生 1000000 个User对象,耗时: 85毫秒
--------4----------
deepCopy产生 1000000 个User对象,耗时: 6602毫秒
cloneBean产生 1000000 个User对象,耗时: 1828毫秒
反射1产生 1000000 个User对象,耗时: 1053毫秒
反射2产生 1000000 个User对象,耗时: 14毫秒
instantiateClass产生 1000000 个User对象,耗时: 54毫秒
--------5----------
deepCopy产生 1000000 个User对象,耗时: 7826毫秒
cloneBean产生 1000000 个User对象,耗时: 2077毫秒
反射1产生 1000000 个User对象,耗时: 1202毫秒
反射2产生 1000000 个User对象,耗时: 15毫秒
instantiateClass产生 1000000 个User对象,耗时: 67毫秒
--------6----------
deepCopy产生 1000000 个User对象,耗时: 7552毫秒
cloneBean产生 1000000 个User对象,耗时: 1719毫秒
反射1产生 1000000 个User对象,耗时: 1010毫秒
反射2产生 1000000 个User对象,耗时: 17毫秒
instantiateClass产生 1000000 个User对象,耗时: 54毫秒
--------7----------
deepCopy产生 1000000 个User对象,耗时: 6238毫秒
cloneBean产生 1000000 个User对象,耗时: 1937毫秒
反射1产生 1000000 个User对象,耗时: 995毫秒
反射2产生 1000000 个User对象,耗时: 13毫秒
instantiateClass产生 1000000 个User对象,耗时: 56毫秒
--------8----------
deepCopy产生 1000000 个User对象,耗时: 6229毫秒
cloneBean产生 1000000 个User对象,耗时: 1768毫秒
反射1产生 1000000 个User对象,耗时: 1162毫秒
反射2产生 1000000 个User对象,耗时: 13毫秒
instantiateClass产生 1000000 个User对象,耗时: 55毫秒
--------9----------
deepCopy产生 1000000 个User对象,耗时: 7094毫秒
cloneBean产生 1000000 个User对象,耗时: 1745毫秒
反射1产生 1000000 个User对象,耗时: 1001毫秒
反射2产生 1000000 个User对象,耗时: 13毫秒
instantiateClass产生 1000000 个User对象,耗时: 55毫秒


通过测试得出结果

  • deepCopy最慢
  • 反射2的性能最快。
    那么这就明白了,Class.forName这个操作效率很低,如果我们能减少这个操作,那么效率就提高了。

Class.forName的改造

Class.forName

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

public class BeanTools {

    private static SoftReference<Map<String, Class<?>>> classPool = new SoftReference<>(
            new HashMap<String, Class<?>>());

    /**
     * 通过类名创建实例
     * @param className
     * @param <T>
     * @return
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static synchronized <T> T createObject(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Map<String, Class<?>> map = classPool.get();
        if (null == map) {
            classPool = new SoftReference<>(
                    new HashMap<String, Class<?>>());
            map = classPool.get();
        }
        Class<?> clazz = map.get(className);
        if (null == clazz) {
            clazz = (Class<T>) Class.forName(className);
            map.put(className, clazz);
        }

        T t = (T) clazz.newInstance();
        return t;
    }
}

正常性测试

@Test
    public void testCreateObj() throws Exception {
        User user2 = BeanTools.createObject("com.it2.springbootredisson.pojo.User");
        user2.setId(2222);
        user2.setName("xiaowang");
        user2.setMap(new HashMap<>());
        user2.getMap().put("email","a@qq.com");

        User user3 = BeanTools.createObject("com.it2.springbootredisson.pojo.User");
        user3.setId(3333);
        user3.setName("xiaoli");
        user3.setMap(new HashMap<>());
        user3.getMap().put("email","b@qq.com");

        System.out.println("----------------------------------");
        System.out.println("--------" + user2);
        System.out.println("--------" + user3);
    }

运行结果

----------------------------------
--------User(id=2222, name=xiaowang, map={email=a@qq.com})
--------User(id=3333, name=xiaoli, map={email=b@qq.com})

可以看到user2和user3是独立对象,避免它们可能是同一个引用。

性能测试

前面的改造完成了,但是最终我们应该以实测来验证结果,是否能提高性能。
前面的测试用例,我们把这种的方式加入到性能对中的测试用例中。

 {
                long s = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    User user3 = BeanTools.createObject("com.it2.springbootredisson.pojo.User");
                    user3.setId(666);
                }
                long e = System.currentTimeMillis();
                System.out.println("BeanTools反射产生 " + n + " 个User对象,耗时: " + (e - s) + "毫秒");
            }

运行测试用例

--------0----------
deepCopy产生 1000000 个User对象,耗时: 9069毫秒
cloneBean产生 1000000 个User对象,耗时: 2059毫秒
反射1产生 1000000 个User对象,耗时: 1026毫秒
反射2产生 1000000 个User对象,耗时: 25毫秒
instantiateClass产生 1000000 个User对象,耗时: 154毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 49毫秒
--------1----------
deepCopy产生 1000000 个User对象,耗时: 7405毫秒
cloneBean产生 1000000 个User对象,耗时: 2177毫秒
反射1产生 1000000 个User对象,耗时: 1200毫秒
反射2产生 1000000 个User对象,耗时: 17毫秒
instantiateClass产生 1000000 个User对象,耗时: 61毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 24毫秒
--------2----------
deepCopy产生 1000000 个User对象,耗时: 6061毫秒
cloneBean产生 1000000 个User对象,耗时: 2035毫秒
反射1产生 1000000 个User对象,耗时: 1084毫秒
反射2产生 1000000 个User对象,耗时: 13毫秒
instantiateClass产生 1000000 个User对象,耗时: 56毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 27毫秒
--------3----------
deepCopy产生 1000000 个User对象,耗时: 7056毫秒
cloneBean产生 1000000 个User对象,耗时: 2242毫秒
反射1产生 1000000 个User对象,耗时: 1359毫秒
反射2产生 1000000 个User对象,耗时: 20毫秒
instantiateClass产生 1000000 个User对象,耗时: 75毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 27毫秒
--------4----------
deepCopy产生 1000000 个User对象,耗时: 7665毫秒
cloneBean产生 1000000 个User对象,耗时: 1781毫秒
反射1产生 1000000 个User对象,耗时: 949毫秒
反射2产生 1000000 个User对象,耗时: 11毫秒
instantiateClass产生 1000000 个User对象,耗时: 49毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 20毫秒
--------5----------
deepCopy产生 1000000 个User对象,耗时: 5904毫秒
cloneBean产生 1000000 个User对象,耗时: 1929毫秒
反射1产生 1000000 个User对象,耗时: 967毫秒
反射2产生 1000000 个User对象,耗时: 11毫秒
instantiateClass产生 1000000 个User对象,耗时: 49毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 20毫秒
--------6----------
deepCopy产生 1000000 个User对象,耗时: 6051毫秒
cloneBean产生 1000000 个User对象,耗时: 1733毫秒
反射1产生 1000000 个User对象,耗时: 1128毫秒
反射2产生 1000000 个User对象,耗时: 12毫秒
instantiateClass产生 1000000 个User对象,耗时: 57毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 22毫秒
--------7----------
deepCopy产生 1000000 个User对象,耗时: 7155毫秒
cloneBean产生 1000000 个User对象,耗时: 1728毫秒
反射1产生 1000000 个User对象,耗时: 1004毫秒
反射2产生 1000000 个User对象,耗时: 12毫秒
instantiateClass产生 1000000 个User对象,耗时: 50毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 20毫秒
--------8----------
deepCopy产生 1000000 个User对象,耗时: 6555毫秒
cloneBean产生 1000000 个User对象,耗时: 2035毫秒
反射1产生 1000000 个User对象,耗时: 1147毫秒
反射2产生 1000000 个User对象,耗时: 14毫秒
instantiateClass产生 1000000 个User对象,耗时: 60毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 25毫秒
--------9----------
deepCopy产生 1000000 个User对象,耗时: 7095毫秒
cloneBean产生 1000000 个User对象,耗时: 2026毫秒
反射1产生 1000000 个User对象,耗时: 1316毫秒
反射2产生 1000000 个User对象,耗时: 29毫秒
instantiateClass产生 1000000 个User对象,耗时: 87毫秒
BeanTools.createObject产生 1000000 个User对象,耗时: 43毫秒


通过上的数据数据,在通过对Class进行缓存后,减少了Class.forName的过程,直接newInstance,这大大提高了性能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值