Как увеличить размер стека 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).



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

749   9  

9 ответов:

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

> 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 исправила эту проблему.

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

Примечание: использование -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/

Я анаграмма упражнение, что бы Изменить Графу проблема, но с 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)

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

Comments

    Ничего не найдено.