如何使用方法句柄调用私有方法?
据我所知,只有两种可公开访问的Lookup实例:
MethodHandles.lookup()MethodHandles.publicLookup()并且都不允许不受限制的私人访问.
有非公众Lookup.IMPL_LOOKUP做我想做的事.是否有一些公共方法来获取它(假设SecurityManager允许它)?
在我的工作中,我们有一个用于指定数学公式的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) Java的MethodHandle.invokeExact(Object ... args)采用可变长度的参数列表.但是,当我尝试传递Object []而不是列表数组时,我收到错误.见下文:
private void doIt() throws Throwable {
Method meth = Foo.class.getDeclaredMethods()[0];
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(meth);
Foo foo = new Foo();
String myStr = "aaa";
Integer myInt = new Integer(10);
Object [] myArray = {foo, myStr, myInt};
mh.invokeExact(foo, myStr, myInt); // prints "Called aaa 10"
mh.invokeExact(myArray); // throws Exception
}
class Foo {
public void bar(String myStr, Integer myInt) {
System.out.println("Called " + myStr + " " + myInt);
}
}
Run Code Online (Sandbox Code Playgroud)
第二次调用invokeExact()会产生以下异常:
Exception in …Run Code Online (Sandbox Code Playgroud) Java代码
package lambda_cache_example_java;
interface Semigroup1<A> {
public A append(A a1, A a2);
}
interface Semigroup2<A> {
public A append(A a1, A a2);
public interface Foo{}
public class Bar{}
}
class Main {
static Semigroup1<Integer> intSemigroup1() {
return (a1, a2) -> a1 + a2;
}
static Semigroup2<Integer> intSemigroup2() {
return (a1, a2) -> a1 + a2;
}
public static void main(String[] args) {
Semigroup1<Integer> x1 = intSemigroup1();
Semigroup1<Integer> x2 = intSemigroup1();
System.out.println(x1);
System.out.println(x2);
System.out.println(x1 == x2); // same instance
Semigroup2<Integer> y1 …Run Code Online (Sandbox Code Playgroud) 我正在通过ASM使用Java字节码,并试图让一个简单的invokedynamic示例正常运行.我觉得好像我从根本上误解了invokedynamic应该如何工作.这是我到目前为止所尝试的:
在Test2.java我有一个静态方法,我希望调用和我的引导方法:
public static int plus(int a, int b) { // method I want to dynamically invoke
return a + b;
}
public static CallSite bootstrap(MethodHandles.Lookup caller, String name,
MethodType type) throws Exception {
MethodHandle mh = MethodHandles.lookup().findStatic(Test2.class,
"plus", MethodType.methodType(int.class, int.class, int.class));
return new ConstantCallSite(mh);
}
Run Code Online (Sandbox Code Playgroud)
现在,在Test.java我生成一个使用ASM Example.class在包中调用的类文件package1:
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitIntInsn(BIPUSH, 42);
mv.visitIntInsn(BIPUSH, 24);
// mv.visitMethodInsn(INVOKESTATIC, "package1/Test2", …Run Code Online (Sandbox Code Playgroud) 我写了一个冒泡排序的实现,与Groovy一起玩,看看是否--indy对性能有任何明显的影响.
从本质上讲,它对一千个随机整数的列表进行了一千次排序,并测量了对列表进行排序的平均执行时间.
列表Integer[]的一半是一个,另一半是一个ArrayList<Integer>.
结果让我很困惑:
$ groovyc BubbleSort.groovy
$ time java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3.jar:. BubbleSort
Average: 22.926ms
Min: 11.202ms
[...] 26.48s user 0.84s system 109% cpu 25.033 total
$ groovyc --indy BubbleSort.groovy
$ time java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3-indy.jar:. BubbleSort
Average: 119.766ms
Min: 68.251ms
[...] 166.05s user 1.52s system 135% cpu 2:03.82 total
Run Code Online (Sandbox Code Playgroud)
查看基准测试运行时的CPU使用情况,编译时的CPU使用率--indy要比非编译时高很多.

这引起了我的兴趣所以我再次运行基准测试 - 但这次启用了Yourkit代理和CPU跟踪.以下是录制的呼叫树:
没有--indy:



groovy
jruby
invokedynamic
java-8
上下文:我一直在对使用invokedynamic和手动生成字节码之间的差异进行基准测试(这是在决定面向 JVM 的编译器是应该发出更冗长的“传统”字节码还是只是invokedynamic使用巧妙的引导方法调用)的上下文中。在这样做时,将字节码映射到MethodHandles至少同样快的组合器是非常简单的,除了tableswitch.
问题:是否有模仿tableswitch使用的技巧MethodHandle?我试着用一个跳转表来模仿它:使用一个常量MethodHandle[],用 索引到它arrayElementGetter,然后用 调用找到的句柄MethodHandles.invoker。然而,当我通过 JMH 运行它时,它最终比原始字节码慢了大约 50%。
下面是生成方法句柄的代码:
private static MethodHandle makeProductElement(Class<?> receiverClass, List<MethodHandle> getters) {
MethodHandle[] boxedGetters = getters
.stream()
.map(getter -> getter.asType(getter.type().changeReturnType(java.lang.Object.class)))
.toArray(MethodHandle[]::new);
MethodHandle getGetter = MethodHandles // (I)H
.arrayElementGetter(MethodHandle[].class)
.bindTo(boxedGetters);
MethodHandle invokeGetter = MethodHandles.permuteArguments( // (RH)O
MethodHandles.invoker(MethodType.methodType(java.lang.Object.class, receiverClass)),
MethodType.methodType(java.lang.Object.class, receiverClass, MethodHandle.class),
1,
0
);
return MethodHandles.filterArguments(invokeGetter, 1, getGetter);
}
Run Code Online (Sandbox Code Playgroud)
这是初始字节码(我试图用一个invokedynamic调用替换它)
public java.lang.Object productElement(int); …Run Code Online (Sandbox Code Playgroud) 从 Kotlin 1.6.0 开始,对于 Kotlin/JVM 项目,可以指定-jvm-target version最高可达 Java 的选项17,请参阅常规和Gradle 插件文档。
这样做有什么好处呢?除了 的默认值之外,我找不到太多关于指定其他值的好处1.8。
我能找到的唯一的东西是:
Record支持 Java,请参阅Kotlin 1.5.0 的发布博客。两者对我来说似乎都可以忽略不计。
特别是因为当指定更高的目标时,人们将失去在使用 Java 1.8 的项目中使用生成的工件的能力,这似乎是不可取的,尤其是对于库而言。
我在这里错过了什么吗?
我目前正在研究这个项目.它运作得非常好.
然而,在重新阅读README之后,我开始怀疑如何记录一些困扰我的东西......
引用这个例子,并忘记抛出异常,它的内容如下:
Files.list(somePath).map(Path::toRealPath).forEach(System.out::println)
Run Code Online (Sandbox Code Playgroud)
好.现在,Path涉及的方法就是这个.当然,我们没有通过任何LinkOption.
再说一遍:让我们忘记它抛出任何异常.
流的.map()需要Function作为参数.此接口用于Function<T, R>定义为:
R apply(T t);
Run Code Online (Sandbox Code Playgroud)
但我使用的方法不接受任何参数.乍一看,它似乎不匹配Function,对吗?除了那个...
它可以写成:
path -> path.toRealPath()
Run Code Online (Sandbox Code Playgroud)
因此,如果方法引用没有参数,或类似的东西,它看起来像使用的机制有点能够调用"流对象"上的方法...
我想相应地记录下来,我在这里遗漏了一些东西.
我错过了什么?
invokedynamic ×10
java ×6
java-8 ×4
methodhandle ×4
jvm ×3
lambda ×3
bytecode ×2
groovy ×2
reflection ×2
.class-file ×1
eclipse ×1
grails ×1
java-7 ×1
jruby ×1
kotlin ×1
scala ×1