TCO в Java — существует ли?

Спойлер: нет

TCO в Java — существует ли?

Проходя собеседования наткнулась на интервьюера, который уверял, что в Java есть TCO начиная с 11 версии, показав такой вот код

try {
  main(args);
} finally {
  main(args);
}

Я конечно же настояла на своём, что не верю, на что мне был дан лишь один ответ: ну он работает и StackOverflowError не возникнет.

Я решила копнуть глубже, посмотрела байткод, а после на всякий случай ещё последила за стековым фреймом (ведь мы, как хорошие разработчики понимаем, что отсутствие ошибки не говорит об отсутствии проблемы), и что же я там увидела

// class version 68.0 (68)
// access flags 0x21
public class Main {

  // compiled from: Main.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 10 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this LMain; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    TRYCATCHBLOCK L0 L1 L2 null
   L0
    LINENUMBER 35 L0
    ALOAD 0
    INVOKESTATIC Main.main ([Ljava/lang/String;)V
   L1
    LINENUMBER 37 L1
    ALOAD 0
    INVOKESTATIC Main.main ([Ljava/lang/String;)V
   L3
    LINENUMBER 38 L3
    GOTO L4
   L2
    LINENUMBER 37 L2
    ASTORE 1
    ALOAD 0
    INVOKESTATIC Main.main ([Ljava/lang/String;)V
   L5
    LINENUMBER 38 L5
    ALOAD 1
    ATHROW
   L4
    LINENUMBER 66 L4
    RETURN
   L6
    LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
    MAXSTACK = 1
    MAXLOCALS = 2
}

А где же оптимизация хвостовой рекурсии? Выглядит как обычная рекурсия, видим invoke внутри меток вместо разворачивания в цикл из изменений переменных и джампов на старт метода.

Следов оптимизации хвостовой рекурсии тут нет, пойдём смотреть что на стеке творится

Думаю слова тут излишни, и с каждой итерацией стек раздувается, а ошибки нет, да просто потому что мы не даём добраться до момента отброса ошибки, по факту это грязный хак 😸