Unused referenced variables always captured in Java

Peter Lawrey :

I wanted to know if this is an implementation detail...

In Java, used local variables are captured for anonymous classes, and lambdas. For anonymous classes, the this is also captured in a non static context whether needed or not.

It appears, however, any local variable referenced is captured even if not used for Oracle JDK 8 update 181.

public static void main(String[] args) {
    Thread t = Thread.currentThread();
    Runnable run = new Runnable() {
        @Override
        public void run() {
            t.yield();
        }
    };
    Runnable run2 = () -> t.yield();
    run.run();
    run2.run();
}

the byte code for the anonymous Runnable is

  // access flags 0x1
  public run()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    GETFIELD UnusedLocalVariable$1.val$t : Ljava/lang/Thread;
    POP
    INVOKESTATIC java/lang/Thread.yield ()V
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE this LUnusedLocalVariable$1; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1

As you can see, it captures the local variable which it loads, but always discards at runtime.

The lambda does much the same, also capturing the variable.

Is this always the case, or an implementation detail?

Holger :

The specification makes no difference between “referencing” and “using” a variable. In that regard, you are using the variable by invoking t.yield(), despite you’re invoking a static method. For this scenario, the specification says

JLS §15.12.4.1, 15.12.4.1. Compute Target Reference (If Necessary):
  • If form is ExpressionName . [TypeArguments] Identifier, then:
    • If the invocation mode is static, then there is no target reference. The ExpressionName is evaluated, but the result is then discarded.
    • Otherwise, the target reference is the value denoted by ExpressionName.

so the behavior is in line with the specification.

While it’s clear that the evaluation must happen, when it has side effects, I would not go so far to conclude that the bytecode sequence ALOAD 0, GETFIELD, POP is strictly required to fulfill the formal rule that the variable is evaluated and the result is discarded, as that code has no effect at all.

But regardless of whether these instruction are present, the variable t is used and hence subject to the formal requirements, i.e. it must be effective final.

Whether this mandated behavior has to result in capturing the value within the instance of the inner class resp. the class generated for the lambda expression, is, perhaps surprisingly, entirely unspecified. The Java Language Specification does not say anything about it.

In other words, while you are asking about a corner case of the value capturing, i.e. the value of a variable which is referenced but not needed, even the general case, i.e. the well-known rule that inner classes always keep a reference to the enclosing this, even if unneeded, while lambda expression do not, does not appear anywhere in the official specification.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=36997&siteId=1