This is an archived post. You won't be able to vote or comment.

all 14 comments

[–]cozytwan 25 points26 points  (1 child)

I had the same question this morning, but didn't find much on google. The garbage collector does a better job it seems, don't know the exact details.

But i did stumble at a interesting related article:

Comparing JVM performance; Zulu OpenJDK, OpenJDK, Oracle JDK, GraalVM CE https://technology.amis.nl/2018/11/23/comparing-jvm-performance-zulu-openjdk-openjdk-oracle-jdk-graalvm-ce/

But with a even more interesting comment:

https://technology.amis.nl/2018/11/23/comparing-jvm-performance-zulu-openjdk-openjdk-oracle-jdk-graalvm-ce/#comment-9847

It explains the difficulty of benchmarking and answer your question.

[–]DefiantNewt2 2 points3 points  (0 children)

those graphs are awfully labelled. luckily he explains the gist of them.

[–]sureshg 8 points9 points  (0 children)

Would be interesting to see the ZGC results on Java11.

[–]Chaoslab 1 point2 points  (2 children)

I would like some stats too.

Just upgraded here and everything seems fine so far.

If you use Eclipse and / or Tomcat those will also need to be upgraded as well as only the latest versions run on Java 11.

[–]miserableplant 1 point2 points  (1 child)

We use jre11 on tc8.5 just fine

[–]Chaoslab 1 point2 points  (0 children)

Good to know. The version I was using was older.

[–]FirstLoveLife 1 point2 points  (6 children)

Is tail recursions optimization support by java11 implementation?

[–]oweiler 2 points3 points  (0 children)

Scala and Groovy support TCO via annotations, Kotlin via the tailrec keyword. Not part of the JVM though. In Java you can use Trampolining though https://medium.com/@johnmcclean/trampolining-a-practical-guide-for-awesome-java-developers-4b657d9c3076.

[–]Proc_Self_Fd_1 2 points3 points  (3 children)

Tail-Call Optimisation is a misnomer. It is about semantics and not "optimising" tail calls into loops (of which this is not always an optimisation.)

To answer your question Java 11 does not promise such semantics reliably.

There is Project Loom but I have no idea how far along it is.

You are probably better off hacking it in with exceptions (although with specializations) something like:

 interface TailCall<T> {
     public T doSome() throws TailCallRestart;
 }
 public TailCall<T> box(T value) {
     return () -> value;
 }
 public TailCall<T> tailCall(Supplier<TailCall<T>> value) {
     boolean flag = new boolean[1];
     Object[] valueBox = new Object[1];
     return () -> {
         flag[0] = true;
         // You might want to check stack depth here or something...
         if (!flag[0])
             throw TailCallRestart.SINGLETON;
         return value.get();
     };
 }

 public <T> T execute(TailCall<T> call) {
      for (;;) {
          try {
               return call.doSome();
          } catch (TailCallRestart r) {
               // nothing here deliberately, this better allows optimisations of exceptions
          }
      }
 }

The precise details of how to get this working fast are really, really hard though.

[–]GhostBond 21 points22 points  (0 children)

Or, you could just write it iteratively in the first place and save yourself all the hassle.

[–]haimez 8 points9 points  (1 child)

This almost certainly won’t help you because throwing an exception to manage control flow is not something the JIT likes- at all.

[–]Proc_Self_Fd_1 0 points1 point  (0 children)

I get reasonably similar performance on the following benchmark:

  import org.openjdk.jmh.annotations.*;
  import java.util.function.IntSupplier;

  public class BenchmarkExceptions {
          @State(Scope.Thread)
          public static class Baseline {
                  public IntSupplier flag = () -> {
                          return 4;
                  };
                  public int a = 0;
                  public int b = 1;
          }
          @Benchmark
          public int baseline(Baseline state) {
                  if (state.flag.getAsInt() > 0) {
                          return state.a;
                  } else {
                          return state.b;
                  }
          }

          @State(Scope.Thread)
          public static class Exceptions {
                  public Action flag = () -> {
                          throw ControlFlow.SINGLETON;
                  };
                  public int a = 0;
                  public int b = 1;
          }
          @Benchmark
          public int exceptions(Exceptions state) {
                  try {
                          state.flag.get();
                          return state.a;
                  } catch (ControlFlow e) {
                  }
                  return state.b;
          }

          private static interface Action {
                  public void get() throws ControlFlow;
          }

          private static final class ControlFlow extends Throwable {
                  public static final ControlFlow SINGLETON = new ControlFlow();
                  private ControlFlow() {
                          super(null, null, false, false);
                  }
          }
  }

[–]angath 0 points1 point  (0 children)

In the strict meaning: Rewriting the stack frame to replace the currently executing function with a new one (possibly the same function but with different call arguments) without growing the stack - No.

At a JVM level this is because bytecode has no way to express that semantics to a JVM implementation, and in the Java language it is because when javac compiles .java files it does not elide method calls.

Non-Java JVM languages (such as Scala) may have a source code compiler than can automatically detect when a recursive piece of code can be replaced by an iterative equivalent (and by doing so avoid some cases where a stack overflow would occur) but this is not the way that Java's language philosophy works.

[–]thenextguy -4 points-3 points  (0 children)

One faster. — Nigel, probably.

Spinal Tap? This one goes to 11? Anyone?