我正在做一个简单的项目,需要检索一个 bean 属性。首先我使用反射。然后我对 invokedynamic 和 Method Handler 进行了一些调查以获得更好的性能。
虽然invokeExact
比反射快得多,但调用比反射慢得多。
测试环境:
- Win7 32位
- Java 7 U 80
- 核心由于 CPU 3.06GHZ
我得到的 tp/ms 是这样的:
mhInvoke * 5 = reflect
reflect * 6 = mhInvokeExact
mhInvokeExact * 10 = direct call
这是性能测试输出(我运行了两次):
Ref tpms = 10479
mh invoke tpms = 273
mh invoke with convert tpms = 957
mh invoke exact tpms = 78033
invoke directly tpms = 883011
Ref tpms = 14181
mh invoke tpms = 282
mh invoke with convert tpms = 984
mh invoke exact tpms = 88768
invoke directly tpms = 883011
这是我的测试代码:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
/**
* User: Mark Zang
* Date: 2015/4/28
* Time: 13:00
*/
public class PerfTestAppMain {
String strVar = String.valueOf(System.currentTimeMillis());
public String getStrVar() {
return strVar;
}
static int count = 1024 * 1024 * 16;
public static void main(String[] args) throws Throwable {
ref();
mhInvoke();
mhInvokeConvert();
mhInvokeExact();
invoke();
System.out.println();
System.out.println();
ref();
mhInvoke();
mhInvokeConvert();
mhInvokeExact();
invoke();
}
static void ref() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
Method ref = PerfTestAppMain.class.getMethod("getStrVar");
Object ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = ref.invoke(bean);
}
long end = System.currentTimeMillis();
System.out.println("Ref tpms = " + ((count) / (end - start)));
}
static void mhInvoke() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class))
.bindTo(bean);
Object ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = mh.invoke();
}
long end = System.currentTimeMillis();
System.out.println("mh invoke tpms = " + ((count) / (end - start)));
}
static void mhInvokeConvert() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class))
.bindTo(bean);
String ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = (String) mh.invoke();
}
long end = System.currentTimeMillis();
System.out.println("mh invoke with convert tpms = " + ((count) / (end - start)));
}
static void mhInvokeExact() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class));
String ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = (String) mh.invokeExact(bean);
}
long end = System.currentTimeMillis();
System.out.println("mh invoke exact tpms = " + ((count) / (end - start)));
}
static void invoke() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
bean.getStrVar();
}
long end = System.currentTimeMillis();
System.out.println("invoke directly tpms = " + ((count) / (end - start + 1)));
}
}
invokeExact
无法满足我的用例,因为我在编译时不知道确切的返回类型。似乎返回类型(强制转换)是MethodHandle
性能的关键。
这似乎不是预期的结果,因为它MethodType
具有确切的返回类型。为什么进行强制施法以提高性能仍然很重要?
是否有一些文档解释了这方面的细节?此外,是否有任何关于比较使用反射与方法处理程序的 impl 细节的文档?