Как увеличить размер стека Java?

я задал этот вопрос, чтобы узнать, как увеличить размер стека вызовов времени выполнения в 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 выше, он работает так с JVM OpenJDK:

$ 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 -Xss18m хватило в 7 пробегов из 10, и -Xss19m тоже не всегда было достаточно, но -Xss20m хватило (в 100 раз из 100). Сбор мусора, запуск JIT или что-то еще вызывают это недетерминированное поведение?

трассировка стека напечатана на StackOverflowError (и, возможно, при других исключениях также) показывает только самые последние 1024 элемента стека выполнения. Ответ ниже демонстрирует, как подсчитать точную достигнутую глубину (которая может быть намного больше 1024).

многие люди, которые ответили, указали, что это хорошая и безопасная практика кодирования, чтобы рассмотреть альтернативные, менее стек-голодные реализации того же алгоритма. В общем случае можно преобразовать набор рекурсивных функций в итеративные функции (используя, например,

9 ответов


Мда... он работает для меня и с гораздо меньшим, чем 999mb стека:

> java -Xss4m Test
0

(Windows JDK 7, build 17.0-B05 client VM и Linux JDK 6-та же информация о версии, что и вы опубликовали)


Я предполагаю, что вы рассчитали "глубину 1024" по повторяющимся строкам в трассировке стека?

очевидно, что длина массива трассировки стека в Throwable, похоже, ограничена 1024. Попробуйте следующую программу:

public class Test {

    public static void main(String[] args) {

        try {
            System.out.println(fact(1 << 15));
        }
        catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was " +
                               e.getStackTrace().length);
        }
    }

    private static int level = 0;
    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }
}

Если вы хотите играть с размером стека потоков, вы захотите посмотреть опцию-Xss в Hotspot JVM. Это может быть что-то другое на не Hotspot VM, так как параметры-X для JVM специфичны для распределения, IIRC.

на Hotspot, это выглядит как java -Xss16M Если вы хотите сделать размер 16 мегабайт.

тип java -X -help Если вы хотите увидеть все конкретные параметры JVM распределения, которые вы можете передать. Я не уверен, что это работает так же на других JVMs, но он печатает все конкретные параметры Hotspot.

для чего это стоит - я бы рекомендовал ограничить использование рекурсивных методов в Java. Это не слишком хорошо для их оптимизации - для одного JVM не поддерживает хвостовую рекурсию (см. предотвращает ли JVM оптимизацию хвостового вызова?). Попробуйте рефакторинг факториального кода выше, чтобы использовать цикл while вместо рекурсивных вызовов методов.


единственный способ контролировать размер стека в процессе-запустить новый Thread. Но вы также можете управлять, создавая самозваный процесс sub Java с помощью


добавить такой вариант

--driver-java-options -Xss512m

для вашей команды spark-submit исправит эту проблему.


трудно дать разумное решение, так как вы стремитесь избежать всех разумных подходов. Рефакторинг одна строка кода является решение senible.

Примечание: использование -Xss устанавливает размер стека каждого потока и является очень плохой идеей.

другой подход-манипуляция байтовым кодом для изменения кода следующим образом;

public static long fact(int n) { 
    return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1); 
}

учитывая, что каждый ответ для n > 127 равен 0. Это позволяет избежать изменения исходного кода.


странно! Вы говорите, что хотите создать рекурсия 1???!!!!

Я бы посоветовал не пробовать. Размер стека будет 2^15 * sizeof(stack-frame). Я не знаю, что такое размер стека, но 2^15-это 32.768. Довольно много... Ну, если он остановится на 1024 (2^10), вам придется сделать его в 2^5 раз больше, это в 32 раза больше, чем с вашей фактической настройкой.


другие плакаты указали, как увеличить память и что вы можете запоминать звонки. Я бы предположил, что для многих приложений вы можете использовать формулу Стирлинга для приближения большого n! очень быстро почти без памяти.

взглянуть на этот пост, который имеет некоторый анализ функции и код:

http://threebrothers.org/brendan/blog/stirlings-approximation-formula-clojure/


Я анаграмма упражнение, что бы Count Change проблема, но с 50 000 купюры (монеты). Я не уверен, что это можно сделать итерационно, мне все равно. Я просто знаю, что опция-xss не имела никакого эффекта-я всегда терпел неудачу после 1024 кадров стека (может быть, scala выполняет плохую работу по доставке на java или ограничению printStackTrace. Не знаю). Это плохой вариант, как объяснили в любом случае. Вы не хотите, чтобы все потоки в приложении были уродливый. Тем не менее, я провел несколько экспериментов с новым потоком (размер стека). Это действительно работает,

  def measureStackDepth(ss: Long): Long = {
    var depth: Long = 0
      val thread: Thread = new Thread(null, new Runnable() {
        override def run() {
          try {
          def sum(n: Long): Long = {depth += 1; if (n== 0) 0 else sum(n-1) + 1}
          println("fact = " + sum(ss * 10))
          } catch {
            case e: StackOverflowError => // eat the exception, that is expected
          }
        }
      }, "deep stack for money exchange", ss)
      thread.start()
      thread.join()
    depth
  }                                               //> measureStackDepth: (ss: Long)Long


  for (ss <- (0 to 10)) println("ss = 10^" +  ss + " allows stack of size " -> measureStackDepth((scala.math.pow (10, ss)).toLong) )
                                                  //> fact = 10
                                                  //| (ss = 10^0 allows stack of size ,11)
                                                  //| fact = 100
                                                  //| (ss = 10^1 allows stack of size ,101)
                                                  //| fact = 1000
                                                  //| (ss = 10^2 allows stack of size ,1001)
                                                  //| fact = 10000
                                                  //| (ss = 10^3 allows stack of size ,10001)
                                                  //| (ss = 10^4 allows stack of size ,1336)
                                                  //| (ss = 10^5 allows stack of size ,5456)
                                                  //| (ss = 10^6 allows stack of size ,62736)
                                                  //| (ss = 10^7 allows stack of size ,623876)
                                                  //| (ss = 10^8 allows stack of size ,6247732)
                                                  //| (ss = 10^9 allows stack of size ,62498160)

вы видите, что стек может расти экспоненциально глубже с экспоненциально большим стеком, выделенным потоку.