更新:由于有些人开始了关于“如何进行基准测试”的毫无意义的讨论,我将强调我的答案中包含的问题的解决方案,现在就在开头:
即使在没有确切类型签名的反射上下文中,也可以通过将 using asType 转换为将 asType
转换为取参数的句柄来使用。在受 和 之间的性能差异影响的环境中,在这样的转换句柄上使用仍然比在直接方法句柄上使用快得多。invokeExact
MethodHandle
Object
invoke
invokeExact
invokeExact
invoke
原答:
问题确实是您没有使用。下面是一个小的基准程序,显示了不同方式递增字段的结果。而不是使用会导致性能下降到反射速度以下。invokeExact
int
invoke
invokeExact
您会收到,因为 是强类型化的。它需要与字段和所有者的类型类型匹配的精确调用签名。但是,您可以使用该句柄创建一个新的包装必要的类型转换。在该句柄上使用泛型签名(即 )仍然比使用动态类型转换更有效。WrongMethodTypeException
MethodHandle
MethodHandle
invokeExact
(Object,Object)Object
invoke
在我的计算机上使用 1.7.0_40 的结果是:
direct : 27,415ns
reflection : 1088,462ns
method handle : 7133,221ns
mh invokeExact: 60,928ns
generic mh : 68,025ns
并使用JVM屈服于令人困惑-server
direct : 26,953ns
reflection : 629,161ns
method handle : 1513,226ns
mh invokeExact: 22,325ns
generic mh : 43,608ns
我不认为它比直接操作更快在现实生活中有太大的相关性,但它证明了Java7上的s并不慢。MethodHandle
MethodHandle
泛型仍然会优于反射(而使用则不会)。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);
}
}
}