java.lang.NoSuchMethodError:VarHandle.compareAndSet(VariableHandlesExample,State,State)void

use*_*018 8 java methodhandle java-9

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)

Jor*_*nee 6

这似乎是JVM/JDK/Spec/Doc中的一个错误,它取决于编译器如何翻译签名多态方法的签名.


compareAndSet标有@MethodHandle.PolymorphicSignature.这意味着在语义上(释义)在调用站点找到的任何签名都将用于调用该方法.这主要是为了防止参数的装箱.

compareAndSetVarHandle 的完整签名是:

public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);
Run Code Online (Sandbox Code Playgroud)

请注意,它返回a boolean,但错误显示VM正在尝试链接VarHandle.compareAndSet(VarHandleExample,int,int)void,它具有不同的返回类型.这可以在Eclipse编译器生成的字节码中看到:

publicIntHandle.compareAndSet(this, 10, 100); // CAS
Run Code Online (Sandbox Code Playgroud)

(部分)翻译为:

25: invokevirtual #55 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)V
Run Code Online (Sandbox Code Playgroud)

(注意那里的注释向我们展示了常量池中用于链接方法的方法引用常量的签名)

因此,在运行时,似乎VM将尝试找到一个方法V(即void)作为返回类型,这确实不存在.

javac 另一方面生成此签名:

25: invokevirtual #11 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)Z
Run Code Online (Sandbox Code Playgroud)

返回类型是Z(含义boolean)而不是V.


您可以boolean通过使用返回值显式地生成返回类型来解决此问题:

boolean b = publicIntHandle.compareAndSet(this, 10, 100); // CAS
Run Code Online (Sandbox Code Playgroud)

或者if,如果您不需要值,请使用空格:

if(publicIntHandle.compareAndSet(this, 10, 100)); // CAS
Run Code Online (Sandbox Code Playgroud)

现在是语言律师的一部分.

我可以找到有关签名多态方法的有限信息(标有@PolymorphicSignature的方法)[ 1 ](语言规范中没有任何内容).似乎没有关于如何在规范中由编译器导出和转换签名多态方法的描述符的任何授权.

也许最有趣的是这段来自jvms-5.4.3.3(强调我的):

如果C只使用方法引用指定的名称声明一个方法,并且声明是签名多态方法(第2.9.3节),则方法查找成功.描述符中提到的所有类名都已解析(第5.4.3.1节).

已解析的方法是签名多态方法声明.C不必声明具有方法引用指定的描述符的方法.

C在这种情况下会VarHandle,该方法的抬头将是compareAndSet和描述符要么(LVarHandleExample;II)Z还是(LVarHandleExample;II)V取决于编译器.

同样有趣的是关于签名多态性的javadoc :

当JVM处理包含签名多态调用的字节码时,无论其符号类型描述符如何,它都将成功链接任何此类调用.(为了保持类型安全性,JVM将使用适当的动态类型检查来保护此类调用,如其他地方所述.)

VarHandle确实只有一个名称的方法,compareAndSet它是签名多态,所以查找应该成功.Imho这是一个问题,在这种情况下抛出异常,因为描述符,因此返回类型应该根据规范无关紧要.

javac Z在描述符中作为返回类型发出似乎也存在问题.根据相同的javadoc部分:

不寻常的部分是符号类型描述符是从实际的参数和返回类型派生的,而不是从方法声明派生的.

但是,javac发出的描述符肯定取决于方法声明.

所以根据spec/doc,这里似乎有2个错误;

  1. 在您正在使用的VM中,它错误地无法链接签名多态方法.我也可以用OpenJDK 64-Bit Server VM (build 13-internal+0-adhoc.Jorn.jdk, mixed mode, sharing)它来重现它,这是最新的OpenJDK源代码.

  2. javac其发出的描述错误的返回类型.

我假设规范是主要权限,但在这种情况下似乎更有可能在实现之后规范/文档没有更新,这就是咬eclipsec的原因.


我收到了回复jdk-dev的电子邮件,Dan Smith 回答说.

对于2.)

javac在这里是正确的.见jls-15.12.3-400-B

"如果签名多态方法为void或者具有除Object之外的返回类型,则编译时结果是编译时声明的调用类型的结果"

你引用的javadoc中的非正式描述是不完整的,我已经提交了一个bug来解决这个问题:https://bugs.openjdk.java.net/browse/JDK-8216511

所以看来Eclipse正在为调用生成错误的描述符,而不是javac.

对于1.)

你是对的.此处未指定链接时NoSuchMethodError.相反,根据VarHandle javadoc,我们应该看到一个运行时WrongMethodTypeException.

错误报告:https://bugs.openjdk.java.net/browse/JDK-8216520