我正在研究JDK 1.7的新功能,我无法得到它为MethodHandle设计的内容?我理解(直接)静态方法的调用(以及在这种情况下直接使用Core Reflection API).我也理解(直接)调用虚方法(非静态,非最终)(以及使用需要通过Class层次结构的Core Reflection API obj.getClass().getSuperclass()).非虚方法的调用可以视为前者的特殊情况.
是的,我知道存在重载问题.如果要调用方法,则必须提供确切的签名.您无法以简单的方式检查重载方法.
但是,MethodHandle是什么?Reflection API允许您"查看"对象内部,而无需任何预先假设(如实现接口).您可以出于某种目的检查对象.但是MethodHandle的设计是什么呢?我应该何时何地使用它?
更新:我现在正在阅读这篇http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html文章.根据它,主要目标是简化在JVM上运行的脚本语言的生命,而不是Java语言本身.
更新-2:我读完了上面的链接,从那里引用了一些引文:
JVM将成为构建动态语言的最佳VM,因为它已经是一个动态语言VM.而InvokeDynamic通过向一流的JVM公民推广动态语言,将证明这一点.
使用反射来调用方法效果很好......除了一些问题.必须从特定类型检索方法对象,并且不能以一般方式创建.<...>
...反射调用比直接调用慢很多.多年来,JVM已经非常擅长快速反映调用.现代JVM实际上在幕后生成了一堆代码,以避免处理大量开销的旧JVM.但简单的事实是,通过任意数量的层反射访问总是比直接调用慢,部分原因是完全通用的"调用"方法必须检查并重新检查接收器类型,参数类型,可见性和其他细节,但因为参数必须都是对象(所以原语是对象框),并且必须作为一个数组提供,以涵盖所有可能的arities(所以参数得到数组框).
对于执行少量反射调用的库,性能差异可能无关紧要,特别是如果这些调用主要是为了在内存中动态设置静态结构,可以进行正常调用.但是在动态语言中,每次调用必须使用这些机制,这是一个严重的性能损失.
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
因此,对于Java程序员来说,它基本上是无用的.我对吗?从这个角度来看,它只能被视为Core Reflection API的替代方式.
我正在写一些调用的代码Field.set和Field.get数千次.显然,由于反射,这是非常缓慢的.
我想看看我是否可以MethodHandle在Java7中提高性能.到目前为止,这就是我所拥有的:
而不是field.set(pojo, value),我正在做:
private static final Map<Field, MethodHandle> setHandles = new HashMap<>();
MethodHandle mh = setHandles.get(field);
if (mh == null) {
mh = lookup.unreflectSetter(field);
setHandles.put(field, mh);
}
mh.invoke(pojo, value);
Run Code Online (Sandbox Code Playgroud)
但是,这似乎没有比使用反射的Field.set调用更好.我在这里做错了吗?
我读到使用invokeExact可能会更快,但当我尝试使用它时,我得到了一个java.lang.invoke.WrongMethodTypeException
有没有人成功地优化了对Field.set或Field.get的重复调用?
随着Java 7的发布MethodHandle,它允许用户像使用其底层字节码一样调用方法.特别是,MethodHandles.Lookup该类提供工厂方法来创建访问类成员的方法句柄:
Lookup对象上的工厂方法对应于方法,构造函数和字段的所有主要用例.工厂方法创建的每个方法句柄都是特定字节码行为的功能等价物.
从功能上讲,这或多或少等同于使用反射来访问这些相同的类成员,但方法句柄比反射更快.
那么,有什么理由仍然使用反射功能一样Field#get(..)/ Method.invoke(..)或这些方法是引进了更快的方法手柄的有效过时了吗?
请注意,尽管在Java 7中引入了方法句柄,但我的问题主要与Java 8有关,在Java 8中,它们被优化为可以达到与直接字段/方法调用大致相等的性能,超过了反射的能力.
假设我在给定类型(类/接口)中有三个方法:
public void foo(Integer integer);
public void foo(Number number);
public void foo(Object object);
Run Code Online (Sandbox Code Playgroud)
使用MethodHandle或反射,我想找到一个对象的最具体的重载方法,该类型的类型仅在运行时已知.即我想在运行时执行JLS 15.12.
例如,假设我在上述类型的方法中包含以下三种方法:
Object object = getLong(); // runtime type is Long *just an example*
MethodHandles.lookup()
.bind(this, "foo", methodType(Void.class, object.getClass()))
.invoke(object);
Run Code Online (Sandbox Code Playgroud)
然后我在概念上会想要foo(Number number)被选中,但上面会抛出一个异常,因为API只会寻找一个foo(Long)方法,而不是别的.请注意,Long此处的用法仅作为示例.对象的类型可以是实践中的任何东西; String,MyBar,Integer,...等等
MethodHandle API中是否有一些东西能够在运行时自动执行与JLS 15.12之后的编译器相同的分辨率?
我写了一个小的基准测试性能java.lang.invoke.MethodHandle,java.lang.reflect.Method和方法的直接调用.
我读到的MethodHandle.invoke()表现几乎与直接通话相同.但我的测试结果显示另一个:MethodHandle调用比反射慢大约三倍.我的问题是什么?可能这是一些JIT优化的结果?
public class Main {
public static final int COUNT = 100000000;
static TestInstance test = new TestInstance();
static void testInvokeDynamic() throws NoSuchMethodException, IllegalAccessException {
int [] ar = new int[COUNT];
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(int.class);
MethodHandle handle = lookup.findStatic(TestInstance.class, "publicStaticMethod", mt) ;
try {
long start = System.currentTimeMillis();
for (int i=0; i<COUNT; i++) {
ar[i] = (int)handle.invokeExact();
}
long stop = System.currentTimeMillis();
System.out.println(ar);
System.out.println("InvokeDynamic time: " + …Run Code Online (Sandbox Code Playgroud) 据我所知,随着Java 7中MethodHandle的引入,引入了编译器生成的方法重载.
MethodHandle的javadoc状态(我已经修剪了示例):
以下是一些用法示例:
Run Code Online (Sandbox Code Playgroud)Object x, y; String s; int i; mh = ... // (Ljava/lang/String;CC)Ljava/lang/String; // (String, char, char) -> String s = (String) mh.invokeExact("daddy",'d','n'); // (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; // (Object, Object, Object) -> Object x = mh.invokeExact((Object)1, (Object)2, (Object)3); // (Ljava/util/List;)I // (List) -> int i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); // (Ljava/io/PrintStream;Ljava/lang/String;)V // (PrintStream, String) -> void mh.invokeExact(System.out, "Hello, world.");上述每个调用都会生成一个invokevirtual指令,其名称为invoke,注释中指示的类型描述符.参数类型直接取自实际参数,而返回类型取自立即应用于调用的强制转换.这个演员可能是一个原始人.如果缺少,则如果在使用返回值的上下文中发生调用,则类型默认为Object.如果调用作为语句发生,则转换是不可能的,并且没有返回类型; 电话无效.
实际上,invokeExact和friends的行为就好像每个可能的参数组合和返回类型都有重载.
我听说MethodHandles正在为Java 8中的功能做准备,比如lambdas.(我知道它们对脚本语言已经很有用了.)
[/介绍]
那么,是否有更多这些编译器生成的重载隐藏在Java中?是否有提示将来会有更多这些提示(例如,使用扩展方法)?为什么首先需要它?仅速度?它如何帮助lambdas(我认为lambdas会编译成一个匿名的内部类)?
简而言之,理由是什么; 为什么它们(生成的重载)现在和将来都有用?
更新:我在这里称之为编译器生成的重载,Oracle人称之为签名多态.
对于我的一个项目,我必须对构造函数进行动态调用.但由于这是Java 7,而不是"经典"反射API,我使用java.lang.invoke.
码:
@ParametersAreNonnullByDefault
public class PathMatcherProvider
{
private static final MethodHandles.Lookup LOOKUP
= MethodHandles.publicLookup();
private static final MethodType CONSTRUCTOR_TYPE
= MethodType.methodType(void.class, String.class);
private final Map<String, Class<? extends PathMatcher>> classMap
= new HashMap<>();
private final Map<Class<? extends PathMatcher>, MethodHandle> handleMap
= new HashMap<>();
public PathMatcherProvider()
{
registerPathMatcher("glob", GlobPathMatcher.class);
registerPathMatcher("regex", RegexPathMatcher.class);
}
public final PathMatcher getPathMatcher(final String name, final String arg)
{
Objects.requireNonNull(name);
Objects.requireNonNull(arg);
final Class<? extends PathMatcher> c = classMap.get(name);
if (c == null)
throw new UnsupportedOperationException();
try {
return …Run Code Online (Sandbox Code Playgroud) 是否可以将方法引用(例如SomeClass::someMethod)转换为MethodHandle实例?我想要编译时检查的好处(确保存在类和方法)以及使用MethodHandleAPI 内省方法的能力.
用例:当且仅当请求未被特定方法触发时(以避免无限递归),我才需要执行代码.我想要一个编译时检查以确保类/方法存在但运行时检查以将调用者与方法进行比较.
所以回顾一下:是否可以将方法引用转换为MethodHandle?
在我的工作中,我们有一个用于指定数学公式的DSL,我们后来应用于很多点(数百万).
截至今天,我们构建公式的AST,并访问每个节点以产生我们称之为"评估者"的东西.然后,我们向该求值程序传递公式的参数,并为每个点进行计算.
例如,我们有这个公式: x * (3 + y)
??????
???????mult???????
? ?????? ?
? ?
???v??? ???v???
? x ? ????? add ????
??????? ? ??????? ?
? ?
???v??? ???v???
? 3 ? ? y ?
??????? ???????
Run Code Online (Sandbox Code Playgroud)
我们的评估员将为每个步骤发出"评估"对象.
这种方法易于编程,但效率不高.
所以我开始研究方法句柄来构建一个"组合"的方法句柄,以加快最近的速度.
这样的事情:我有我的"算术"课:
public class Arithmetics {
public static double add(double a, double b){
return a+b;
}
public static double mult(double a, double b){
return a*b;
}
}
Run Code Online (Sandbox Code Playgroud)
在构建我的AST时,我使用MethodHandles.lookup()来直接获取它们的句柄并组合它们.沿着这些方向的东西,但在一棵树上:
Method add = ArithmeticOperator.class.getDeclaredMethod("add", double.class, double.class);
Method mult = ArithmeticOperator.class.getDeclaredMethod("mult", double.class, …Run Code Online (Sandbox Code Playgroud) VarHandle显示以下错误 -
Exception in thread "main" java.lang.NoSuchMethodError: VarHandle.compareAndSet(VarHandleExample,int,int)void
at java.base/java.lang.invoke.MethodHandleNatives.newNoSuchMethodErrorOnVarHandle(MethodHandleNatives.java:492)
at java.base/java.lang.invoke.MethodHandleNatives.varHandleOperationLinkerMethod(MethodHandleNatives.java:445)
at java.base/java.lang.invoke.MethodHandleNatives.linkMethodImpl(MethodHandleNatives.java:378)
at java.base/java.lang.invoke.MethodHandleNatives.linkMethod(MethodHandleNatives.java:366)
at j9.VarHandleExample.update(VarHandleExample.java:23)
at j9.VarHandleExample.main(VarHandleExample.java:14)
Run Code Online (Sandbox Code Playgroud)
我的计划是:
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class VarHandleExample {
public int publicTestVariable = 10;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
VarHandleExample e= new VarHandleExample();
e.update();
}
public void update() throws NoSuchFieldException, IllegalAccessException {
VarHandle publicIntHandle = MethodHandles.lookup()
.in(VariableHandlesTest.class)
.findVarHandle(VarHandleExample.class, "publicTestVariable", int.class);
publicIntHandle.compareAndSet(this, 10, 100); // CAS
}
}
Run Code Online (Sandbox Code Playgroud) methodhandle ×10
java ×9
java-8 ×4
java-7 ×3
performance ×3
reflection ×3
dynamic ×1
field ×1
java-9 ×1
jvm ×1
lambda ×1