当前位置: 首页 > 知识库问答 >
问题:

java.lang.的方法错误:VarHandle.compareAndSet(变量句柄示例,状态,状态)无效

茹元魁
2023-03-14

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)

我的计划是:

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
    }
}

共有1个答案

汪理
2023-03-14

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

compareAndSet@METHODHandle标记。多态签名.这意味着在语义上(意译),无论在调用站点上发现什么签名,都将被用来调用该方法。这主要防止了争论的拳击。

VarHandle中compareAndSet的完整签名为:

public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);

请注意,它返回一个布尔值,但错误显示VM正在尝试链接VarHandle。比较数据集(VarHandleExample,int,int)void,它具有不同的返回类型。这也可以在Eclipse编译器生成的字节码中看到:

publicIntHandle.compareAndSet(this, 10, 100); // CAS

(部分)翻译为:

25: invokevirtual #55 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)V

(注意这里的注释,它显示了常量池中方法引用常量的签名,该常量池用于链接该方法)

因此,在运行时,VM似乎会尝试找到一个返回类型为V(即void)的方法,而该方法实际上并不存在。

另一方面,javac会生成这个签名:

25: invokevirtual #11 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)Z

其中返回类型为Z(意思是布尔值),而不是V

您可以通过显式地创建返回类型boolean来解决这个问题,或者使用返回值:

boolean b = publicIntHandle.compareAndSet(this, 10, 100); // CAS

或者,如果不需要该值,则使用空白的(如果):

if(publicIntHandle.compareAndSet(this, 10, 100)); // CAS

现在进入语言律师部分。

我可以找到关于签名多态方法的有限信息(那些标有@多态性签名的方法)[1](在语言规范中没有)。对于签名多态方法的描述符应该如何由编译器在规范中派生和翻译,似乎没有任何授权。

也许最有趣的是jvms-5.4.3.3(我的重点)中的这一段:

如果C声明了一个方法,名称由方法引用指定,并且声明是一个签名多态方法(§2.9.3),那么方法查找成功。描述符中提到的所有类名都被解析(§5.4.3.1)。

解析的方法是签名多态方法声明。C不需要用方法引用指定的描述符声明方法。

在这种情况下,其中C将是VarHandle,查找的方法将是compareAndSet,描述符可以是(LVarHandle示例;II)Z(LVarHandle示例;II)V,具体取决于编译器...

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

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

VarHandle确实有一个名为compareAndSet的方法,并且它是签名多态的,所以查找应该会成功。恕我直言,在这种情况下引发异常是VM的一个问题,因为描述符,因此根据规范返回类型不重要。

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

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

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

因此,根据规范/文档,这里似乎有两个错误;

>

  • 在使用的VM中,错误地无法链接签名多态方法。我也可以用OpenJDK 64-Bit Server VM(build 13-内部0-adhoc)重现它。Jorn.jdk,混合模式,共享),这是最新的OpenJDK源码。

    在javac中为描述符发出错误的返回类型。

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

    Dan Smith回复了我给jdk dev的电子邮件。

    对于2.)

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

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

    您引用的javadoc中的非正式描述不完整,我已经提交了一个bug来修复:https://bugs.openjdk.java.net/browse/JDK-8216511

    因此,Eclipse似乎为调用生成了错误的描述符,而不是javac。

    1.)

    你是正确的。此处未指定链接时NoSuchMultiodError。相反,根据VarHandle javadoc,我们应该看到运行时错误方法类型异常。

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

  •  类似资料:
    • 在接口流中: 中间操作可分为有状态和无状态。它们影响并行流的结果。 只有两个终端操作是非确定性方法:findAny()和forEach(Consumer)。它们影响并行流的结果。 如果中间无状态操作执行惰性操作,它们可能会产生副作用。这会影响并行Stream的结果。 中间操作可分为以下几类: 有状态 不同的() 排序() 极限(长l) 跳过(长l) 无国籍 地图(功能f) 以下是我的两个问题: >

    • 运行Spark 1.3.1和1.4.1时出现以下错误 Java语言lang.NoSuchMethodError:拼花地板。前提条件。检查状态(ZLjava/lang/String;)镶木地板处为V。架构。类型$PrimitiveBuilder。在拼花地板上建造(Types.java:314)。架构。类型$PrimitiveBuilder。在拼花地板上建造(类型:java:232)。架构。类型$生成

    • 原始状态是操作员在自己数据结构中保持的状态。当检查点为检查点时,它们只将一系列字节写入检查点。Flink对状态的数据结构一无所知,只看到原始字节。 然而,我没有找到任何例子来突出这种区别。有谁能提供一个最小的例子,在代码中明确区别吗?

    • 问题内容: 我试图将远程句柄持久化为有状态EJB3.0 bean。该bean的接口已定义: 实现是: 根据EJB Spec,那应该抓住我一个可序列化的句柄。但是我得到了: 我不确定我理解我做错了什么…所有有状态的bean应该具有可序列化的句柄。在EJB3.0中是否有“正确”的方法来获取可序列化的句柄? 问题答案: getEJBObject不适用于EJB 3.0编程模型。您将需要使用@RemoteH

    • EJB2.0对象处理(从getHandle()检索)如何帮助在从服务器断开连接后恢复状态bean? 在我们的应用程序体系结构中,我们将有状态bean的句柄保留在客户端的缓存中。如果集群中创建有状态bean的节点由于某种原因关闭,句柄会将下一次对有状态bean的调用重定向到新节点吗?有没有任何配置,我们可以做,如果一个节点关闭,然后重新路由调用的状态Bean到集群中的另一个节点?

    • 我是EJB的新手,最近开始研究EJB(3.0)。我已经使用Java6年了,但以前从未使用过EJB。至少可以说,整个EJB业务的复杂性让我不知所措。我不明白我可以在哪里实际应用一些概念。 在理解无状态会话bean后,我想到的一个问题是,你能不能不要总是用一个没有本地成员的类的共享实例来替换无状态会话bean(实际上使其无状态)?我了解到正在为无状态会话bean进行实例池。如果没有状态,就不能简单地使