天天看點

如何提高java反射效率

1、在系統啟動階段使用反射。

2、将反射得到中繼資料儲存起來,使用時,隻需從記憶體中調用即可。

3、hotspot虛拟機會對執行次數較多的方法進行優化(例如使用jit技術)。

4、使用高性能的反射庫,應該會比自己寫緩存效果好。

準備測試對象

下面先定義一個測試的類TestUser,隻有id跟name屬性,以及它們的getter/setter方法,另外還有一個自定義的sayHi方法。

public class TestUser { private Integer id; private String name;

public String sayHi(){ return “hi”;

} public Integer getId() { return id;

} public void setId(Integer id) { this.id = id;

} public String getName() { return name;

} public void setName(String name) { this.name = name;

}

測試建立100萬個對象

// 通過普通方式建立TestUser對象@Testpublic void testCommon(){ long start = System.currentTimeMillis();

TestUser user = null; int i = 0; while(i<1000000){

++i;

user = new TestUser();

} long end = System.currentTimeMillis();

System.out.println(“普通對象建立耗時:”+(end - start ) + “ms”);

}//普通對象建立耗時:10ms

// 通過反射方式建立TestUser對象@Testpublic void testReflexNoCache() throws Exception { long start = System.currentTimeMillis();

user = (TestUser) Class.forName(“ReflexDemo.TestUser”).newInstance();

System.out.println(“無緩存反射建立對象耗時:”+(end - start ) + “ms”);

}//無緩存反射建立對象耗時:926ms

在上面這兩個測試方法中,筆者各自測了5次,把他們消耗的時間取了一個平均值,在輸出結果中可以看到一個是10ms,一個是926ms,在建立100W個對象的情況下,反射居然慢了90倍左右。wtf?差距居然這麼大?難道反射真的這麼慢?下面筆者換一種反射的姿勢,繼續測試一下,看看結果如何

// 通過緩存反射方式建立TestUser對象@Testpublic void testReflexWithCache() throws Exception { long start = System.currentTimeMillis();

TestUser user = null;

Class rUserClass = Class.forName(“RefleDemo.TestUser”); int i = 0; while(i<1000000){

user = (TestUser) rUserClass.newInstance();

System.out.println(“通過緩存反射建立對象耗時:”+(end - start ) + “ms”);

}//通過緩存反射建立對象耗時:41ms

其實通過代碼我們可以發現,是Class.forName這個方法比較耗時,它實際上調用了一個本地方法,通過這個方法來要求JVM查找并加載指定的類。是以我們在項目中使用的時候,可以把Class.forName傳回的Class對象緩存起來,下一次使用的時候直接從緩存裡面擷取,這樣就極大的提高了擷取Class的效率。同理,在我們擷取Constructor、Method等對象的時候也可以緩存起來使用,避免每次使用時再來耗費時間建立。

測試反射調用方法

@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis();

Class testUserClass = Class.forName(“RefleDemo.TestUser”);

TestUser testUser = (TestUser) testUserClass.newInstance();

Method method = testUserClass.getMethod(“sayHi”); int i = 0; while(i<100000000){

method.invoke(testUser);

System.out.println(“反射調用方法耗時:”+(end - start ) + “ms”);

}//反射調用方法耗時:330ms

method.setAccessible(true);

System.out.println(“setAccessible=true 反射調用方法耗時:”+(end - start ) + “ms”);

}//setAccessible=true 反射調用方法耗時:188ms

這裡我們反射調用sayHi方法1億次,在調用了method.setAccessible(true)後,發現快了将近一半。檢視API可以了解到,jdk在設定擷取字段,調用方法的時候會執行安全通路檢查,而此類操作會比較耗時,是以通過setAccessible(true)的方式可以關閉安全檢查,進而提升反射效率。

極緻的反射