RSS Amplifier

ivo's awfully random tech blog · Jun 20, 2018

TIL: Java hides lambda frames in stack traces

0
Sign in to vote or save

ivoanjo.me

While playing around with Java stack traces today, I noticed something that I had never noticed before: lambda frames do not show up in stack traces!

For instance, consider the following pre-Java 8 example:

public class VisibleLambda {
  public static void main(String[] args) {
    foo(new Runnable() {
      public void run() {
        bar();
      }
    });
  }
  static void foo(Runnable lambda) {
    lambda.run();
  }
  static void bar() {
    throw new RuntimeException();
  }
}

In the main method, we create an anonymous inner class that implements Runnable and calls bar when run.

The resulting output for this program is as expected:

Exception in thread "main" java.lang.RuntimeException
        at VisibleLambda.bar(VisibleLambda.java:15)
        at VisibleLambda$1.run(VisibleLambda.java:5)
        at VisibleLambda.foo(VisibleLambda.java:11)
        at VisibleLambda.main(VisibleLambda.java:3)

Checks out: main calls foo, which then calls the Runnable::run method (here implemented by a class automatically named VisibleLambda$1), which then calls bar. So far, so good, and this matched my understanding of how this works together.

But consider now what happens when we switch to using a Java 8 lambda:

public class HiddenLambda {
  public static void main(String[] args) {
    foo(() -> bar());
  }
  static void foo(Runnable lambda) {
    lambda.run();
  }
  static void bar() {
    throw new RuntimeException();
  }
}

…​and the output is:

Exception in thread "main" java.lang.RuntimeException
        at HiddenLambda.bar(HiddenLambda.java:11)
        at HiddenLambda.lambda$main$0(HiddenLambda.java:3)
        at HiddenLambda.foo(HiddenLambda.java:7)
        at HiddenLambda.main(HiddenLambda.java:3)

Did you see it? Two interesting things happened here. One is that a new method named lambda$main$0 appeared in the HiddenLambda class. The other is that our Runnable is gone — nowhere on the stack we can see a run method being called, although it is definitely there.

So what’s the lambda$main$0? Using javap -p -c HiddenLambda we can see that it contains the code to our little lambda:

public class HiddenLambda {
  private static void lambda$main$0();
    Code:
       0: invokestatic  #7                  // Method bar:()V
       3: return
}

After reading a bit more of the source code, I’ve discovered that behavior can be controlled with command-line options: -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames.

So finally, here it is in action:

$ java -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames HiddenLambda
Exception in thread "main" java.lang.RuntimeException
        at HiddenLambda.bar(HiddenLambda.java:11)
        at HiddenLambda.lambda$main$0(HiddenLambda.java:3)
        at HiddenLambda$$Lambda$1/791452441.run(<Unknown>:1000000)
        at HiddenLambda.foo(HiddenLambda.java:7)
        at HiddenLambda.main(HiddenLambda.java:3)

Read the original on ivoanjo.me

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.

    Reading · ivo's awfully random tech blog · RSS Amplifier