在这种方法调用和传入参数的情况下,Java 评估顺序是不是得到保证

Posted

技术标签:

【中文标题】在这种方法调用和传入参数的情况下,Java 评估顺序是不是得到保证【英文标题】:Is Java evaluation order guaranteed in this case of method call and arguments passed in在这种方法调用和传入参数的情况下,Java 评估顺序是否得到保证 【发布时间】:2013-08-13 10:06:15 【问题描述】:

我对 JLS 15.7.4 和 15.12.4.2 进行了一些阅读,但它不能保证不会有任何 编译器/运行时优化改变 em> 计算方法参数的顺序。

假设以下代码:

public static void main (String[] args) 
  MyObject obj = new MyObject();
  methodRelyingOnEvalOrder(obj, obj.myMethod());


public static Object methodRelyingOnEvalOrder(MyObject obj, Object input) 
  if (obj.myBoolean())
    return null;
  else
    return input;

是否保证编译器或运行时不会进行如下错误优化?这种优化可能看起来是正确的,但当评估顺序很重要时,它就是错误的。

如果调用obj.myMethod 会改变obj.myBoolean 返回的值,那么首先调用obj.myMethod 是至关重要的,因为methodRelyingOnEvalOrder 需要先进行此更改。

//*******************************
//Unwanted optimization possible:
//*******************************
public static void main (String[] args) 
  MyObject obj = new MyObject();
  methodRelyingOnEvalOrder(obj);


public static Object methodRelyingOnEvalOrder(MyObject obj) 
  if (obj.myBoolean())
    return null;
  else
    return obj.myMethod();

//*******************************

如果可能,请提供一些支持您的答案的资源或 Java 文档。

注意:请不要要求重写代码。这是我质疑评估顺序保证和编译器/运行时优化保证的特定情况。 obj.myMethod 的执行必须发生在 main 方法中。

【问题讨论】:

您可以确定javac 几乎没有进行任何优化,这一切都是由 JIT 完成的。虽然它可能会重新排序甚至消除方法调用,但最终行为应该是相同的。 【参考方案1】:

您提到的 JLS 部分 (15.7.4) 确实保证:

每个参数表达式似乎都在其右侧的任何参数表达式的任何部分之前被完全评估。

以及在 15.12.4.2 中:

然后使用参数值继续评估,如下所述。

“看起来”部分允许进行一些优化,但它不能可见。所有参数在之前“评估然后继续”的事实表明,在方法执行之前,参数确实被完全评估。 (或者至少,这是可见的结果。)

例如,如果您有以下代码:

int x = 10;
foo(x + 5, x + 20);

可能优化它以并行评估 x + 5x + 20:你无法检测到这种情况。

但在您给出的情况下,您能够检测到在调用 obj.myBoolean() 之后发生的对 obj.myMethod 的调用,因此 不会 em> 完全是一个有效的优化。

简而言之:您可以假设一切都将以明显的方式执行。

【讨论】:

您能否进一步解释JLS 似乎是 的含义以及为什么它不是is?不可见是什么意思? @ADTC:这意味着您将无法在此处检测任何优化 - 如果应用了任何优化,这是一个真正不可见的优化。 我的直觉同意,但是该部分实际上说的是If the evaluation of any argument expression completes abruptly, then no part of any argument expression to its right appears to have been evaluated.,至少从我在该部分中看到的内容来看,它没有明确指定明显的评估顺序如果没有一个评估突然完成,应该是。 @JasonC:不,那是不同的段落。我引用的位不取决于您引用的位。这是两个独立的保证。 (或者更确切地说,我会说您引用的内容只是澄清了我在出现异常时引用的内容。它绝不会说“如果没有例外,则上一段不适用抛出。”) @ADTC:正如 user2758929 在他的回答中指出的那样,您提出的“不需要的优化”是不可能的。如果myMethod 在您的第一个示例中抛出,myBoolean 将永远不会被执行,因为参数评估必须首先出现。【参考方案2】:

除了参数评估顺序之外,在15.12.4. Run-Time Evaluation of Method Invocation 部分中解释的调用方法时执行的步骤的概述及其顺序清楚地表明所有参数评估都在之前执行执行方法的代码。引用:

在运行时,方法调用需要五个步骤。首先,一个目标 可以计算参考。 其次,参数表达式为 评估。三、被调用方法的可访问性是 检查。四、要执行的方法的实际代码是 位于。 五、创建一个新的激活帧,同步是 必要时执行,并将控制转移到方法代码

您提出的仅在控制权转移到方法代码之后评估参数的情况是不可能的。

【讨论】:

感谢您的贡献。我只能接受一个答案,但我支持你:)

以上是关于在这种方法调用和传入参数的情况下,Java 评估顺序是不是得到保证的主要内容,如果未能解决你的问题,请参考以下文章

java 重载

Java中的泛型理解

何时在方法内创建传入的参数的副本,何时不?

模型的性能评估 用sklearn进行模型评估

按名称传递参数

04 JVM是如何执行方法调用的(上)