文章目录
- 前言
- 反射性能到底如何?
- 创建对象的5种办法
- Class.forName的改造
- Class.forName
- 正常性测试
- 性能测试
前言
我们都知道,通过类名反射创建实例的性能很差。
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,这大大提高了性能。