Я задал этот вопрос, чтобы узнать, как увеличить размер стека вызовов во время выполнения в JVM. У меня есть ответ на этот вопрос, и у меня также есть много полезных ответов и комментариев, касающихся того, как Java обрабатывает ситуацию, когда требуется большой стек времени выполнения. Я распространил свой вопрос с резюме ответов.
Изначально я хотел увеличить размер стека JVM, чтобы программы запускались без StackOverflowError.
public class TT {
  public static long fact(int n) {
    return n < 2 ? 1 : n * fact(n - 1);
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}
Соответствующим параметром конфигурации является флаг командной строки java -Xss... с достаточно большим значением. Для программы TT выше, она работает следующим образом с OpenJDK JVM:
$ javac TT.java
$ java -Xss4m TT
В одном из ответов также указано, что флаги -X... зависят от реализации. Я использовал
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)
Также можно указать большой стек только для одного потока (см. один из ответов, как). Это рекомендуется для java -Xss..., чтобы избежать потери памяти для потоков, которые не нуждаются в ней.
Мне было любопытно, насколько большой стек имеет нужную программу, поэтому я запустил ее n увеличено:
-  -Xss4m может быть достаточно для fact(1 << 15)
-  -Xss5m может быть достаточно для fact(1 << 17)
-  -Xss7m может быть достаточно для fact(1 << 18)
-  -Xss9m может быть достаточно для fact(1 << 19)
-  -Xss18m может быть достаточно для fact(1 << 20)
-  -Xss35m может быть достаточно для fact(1 << 21)
-  -Xss68m может быть достаточно для fact(1 << 22)
-  -Xss129m может быть достаточно для fact(1 << 23)
-  -Xss258m может быть достаточно для fact(1 << 24)
-  -Xss515m может быть достаточно для fact(1 << 25)
Из приведенных выше чисел кажется, что Java использует около 16 байт на стек кадров для функции выше, что разумно.
Недостаточно перечисленного перечисления может быть достаточно, потому что требование стека не является детерминированным: он выполняется несколько раз с одним и тем же исходным файлом, а тот же -Xss... иногда преуспевает, а иногда и дает StackOverflowError. Например. дл 1 < 20, -Xss18m было достаточно в 7 пробегах из 10, и -Xss19m также не всегда было достаточно, но -Xss20m было достаточно (всего 100 закончилось из 100). Собирает ли сбор мусора, JIT, или что-то еще, вызывает это недетерминированное поведение?
Трассировка стека, напечатанная на StackOverflowError (и, возможно, с другими исключениями), показывает только самые последние 1024 элемента стека времени выполнения. Ответ ниже показывает, как подсчитать достигнутую точную глубину (которая может быть намного больше 1024).
Многие люди, которые ответили, отметили, что хорошая и безопасная практика кодирования рассматривает альтернативные, менее утомительные реализации одного и того же алгоритма. В общем случае можно преобразовать в набор рекурсивных функций к итеративным функциям (используя, например, объект Stack, который заполняется в куче, а не в стеке времени выполнения). Для этой конкретной функции fact ее легко преобразовать. Моя итеративная версия будет выглядеть так:
public class TTIterative {
  public static long fact(int n) {
    if (n < 2) return 1;
    if (n > 65) return 0;  // Enough powers of 2 in the product to make it (long)0.
    long f = 2;
    for (int i = 3; i <= n; ++i) {
      f *= i;
    }
    return f;
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}
FYI, как показывает приведенное выше итерационное решение, функция fact не может вычислить точный факториал чисел выше 65 (фактически, даже выше 20), потому что встроенный тип long Java будет переполняться. Рефакторинг fact, чтобы он возвращал BigInteger вместо long, также дал бы точные результаты для больших входов.
