更新:由于有些人开始了关于“如何进行基准测试”的毫无意义的讨论,我将强调我的答案中包含的问题的解决方案,现在就在开头:
通过将using转换为作为参数的句柄,您甚至可以invokeExact
在没有确切类型签名的反射上下文中使用。在受和之间的性能差异影响的环境中,在这种转换句柄上使用仍然比在直接方法句柄上使用快得多。MethodHandle
asType
Object
invoke
invokeExact
invokeExact
invoke
原答案:
问题确实是您没有使用invokeExact
. 下面是一个小基准程序,显示了增加int
字段的不同方式的结果。使用invoke
而不是invokeExact
导致性能下降到反射速度以下。
您收到 是WrongMethodTypeException
因为MethodHandle
是强类型的。它期望与字段和所有者的类型类型匹配的精确调用签名。但是您可以使用句柄创建一个新MethodHandle
的包装必要的类型转换。使用invokeExact
泛型签名(即(Object,Object)Object
)在该句柄上使用仍然比使用invoke
动态类型转换更有效。
在我的机器上使用 1.7.0_40 的结果是:
直接:27,415ns
反射:1088,462ns
方法句柄:7133,221ns
mh 调用精确:60,928ns
通用 mh : 68,025ns
并且使用-server
JVM 会让人感到莫名其妙
直接:26,953ns
反射:629,161ns
方法句柄:1513,226ns
mh 调用精确:22,325ns
通用 mh : 43,608ns
我认为看到 aMethodHandle
比直接操作更快并没有太大的现实意义,但它证明MethodHandle
s 在 Java7 上并不慢。
并且泛型MethodHandle
仍然会优于反射(而使用invoke
不会)。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
public class FieldMethodHandle
{
public static void main(String[] args)
{
final int warmup=1_000_000, iterations=1_000_000;
for(int i=0; i<warmup; i++)
{
incDirect();
incByReflection();
incByDirectHandle();
incByDirectHandleExact();
incByGeneric();
}
long direct=0, refl=0, handle=0, invokeExact=0, genericH=0;
for(int i=0; i<iterations; i++)
{
final long t0=System.nanoTime();
incDirect();
final long t1=System.nanoTime();
incByReflection();
final long t2=System.nanoTime();
incByDirectHandle();
final long t3=System.nanoTime();
incByDirectHandleExact();
final long t4=System.nanoTime();
incByGeneric();
final long t5=System.nanoTime();
direct+=t1-t0;
refl+=t2-t1;
handle+=t3-t2;
invokeExact+=t4-t3;
genericH+=t5-t4;
}
final int result = VALUE.value;
// check (use) the value to avoid over-optimizations
if(result != (warmup+iterations)*5) throw new AssertionError();
double r=1D/iterations;
System.out.printf("%-14s:\t%8.3fns%n", "direct", direct*r);
System.out.printf("%-14s:\t%8.3fns%n", "reflection", refl*r);
System.out.printf("%-14s:\t%8.3fns%n", "method handle", handle*r);
System.out.printf("%-14s:\t%8.3fns%n", "mh invokeExact", invokeExact*r);
System.out.printf("%-14s:\t%8.3fns%n", "generic mh", genericH*r);
}
static class MyValueHolder
{
int value;
}
static final MyValueHolder VALUE=new MyValueHolder();
static final MethodHandles.Lookup LOOKUP=MethodHandles.lookup();
static final MethodHandle DIRECT_GET_MH, DIRECT_SET_MH;
static final MethodHandle GENERIC_GET_MH, GENERIC_SET_MH;
static final Field REFLECTION;
static
{
try
{
REFLECTION = MyValueHolder.class.getDeclaredField("value");
DIRECT_GET_MH = LOOKUP.unreflectGetter(REFLECTION);
DIRECT_SET_MH = LOOKUP.unreflectSetter(REFLECTION);
GENERIC_GET_MH = DIRECT_GET_MH.asType(DIRECT_GET_MH.type().generic());
GENERIC_SET_MH = DIRECT_SET_MH.asType(DIRECT_SET_MH.type().generic());
}
catch(NoSuchFieldException | IllegalAccessException ex)
{
throw new ExceptionInInitializerError(ex);
}
}
static void incDirect()
{
VALUE.value++;
}
static void incByReflection()
{
try
{
REFLECTION.setInt(VALUE, REFLECTION.getInt(VALUE)+1);
}
catch(IllegalAccessException ex)
{
throw new AssertionError(ex);
}
}
static void incByDirectHandle()
{
try
{
Object target=VALUE;
Object o=GENERIC_GET_MH.invoke(target);
o=((Integer)o)+1;
DIRECT_SET_MH.invoke(target, o);
}
catch(Throwable ex)
{
throw new AssertionError(ex);
}
}
static void incByDirectHandleExact()
{
try
{
DIRECT_SET_MH.invokeExact(VALUE, (int)DIRECT_GET_MH.invokeExact(VALUE)+1);
}
catch(Throwable ex)
{
throw new AssertionError(ex);
}
}
static void incByGeneric()
{
try
{
Object target=VALUE;
Object o=GENERIC_GET_MH.invokeExact(target);
o=((Integer)o)+1;
o=GENERIC_SET_MH.invokeExact(target, o);
}
catch(Throwable ex)
{
throw new AssertionError(ex);
}
}
}