¿Cómo aumentar el tamaño de la pila de Java?

Hice esta pregunta para saber cómo aumentar el tamaño de la pila de llamadas en tiempo de ejecución en la JVM. Tengo una respuesta a esto, y también tengo muchas respuestas útiles y comentarios relevantes sobre cómo Java maneja la situación en la que se necesita una gran pila de tiempo de ejecución. He extendido mi pregunta con el resumen de las respuestas.

Originalmente quería aumentar el tamaño de la pila JVM para que programas como ejecuciones sinStackOverflowError.

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));
  }
}

La configuración correspondiente es lajava -Xss... bandera de línea de comando con un valor suficientemente grande. Para el programaTT arriba, funciona así con JVM de OpenJDK:

$ javac TT.java
$ java -Xss4m TT

Una de las respuestas también ha señalado que el-X... Las banderas dependen de la implementación. Yo estaba usando

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)

También es posible especificar una pila grande solo para un hilo (vea en una de las respuestas cómo). Esto se recomienda sobrejava -Xss... para evitar el desperdicio de memoria para hilos que no lo necesitan.

Tenía curiosidad por saber qué tan grande necesita exactamente el programa anterior, así que lo he ejecutadon aumentado:

-Xss4m puede ser suficiente parafact(1 << 15)-Xss5m puede ser suficiente parafact(1 << 17)-Xss7m puede ser suficiente parafact(1 << 18)-Xss9m puede ser suficiente parafact(1 << 19)-Xss18m puede ser suficiente parafact(1 << 20)-Xss35m puede ser suficiente parafact(1 << 21)-Xss68m puede ser suficiente parafact(1 << 22)-Xss129m puede ser suficiente parafact(1 << 23)-Xss258m puede ser suficiente parafact(1 << 24)-Xss515m puede ser suficiente parafact(1 << 25)

Según los números anteriores, parece que Java está utilizando aproximadamente 16 bytes por marco de pila para la función anterior, lo cual es razonable.

La enumeración anterior contienepuede ser suficiente en lugar dees suficiente, porque el requisito de pila no es determinista: ejecutarlo varias veces con el mismo archivo de origen y el mismo-Xss... a veces tiene éxito y a veces produce unStackOverflowError. P.ej. para 1 << 20,-Xss18m fue suficiente en 7 carreras de 10, y-Xss19m tampoco siempre fue suficiente, pero-Xss20m fue suficiente (en total 100 carreras de 100). ¿La recolección de basura, la entrada en funcionamiento del JIT o algo más causan este comportamiento no determinista?

El rastro de la pila impreso en unStackOverflowError (y posiblemente también en otras excepciones) muestra solo los 1024 elementos más recientes de la pila de tiempo de ejecución. Una respuesta a continuación muestra cómo contar la profundidad exacta alcanzada (que podría ser mucho mayor que 1024).

Muchas personas que respondieron han señalado que es una práctica de codificación buena y segura considerar implementaciones alternativas, menos halagüeñas, del mismo algoritmo. En general, es posible convertir a un conjunto de funciones recursivas a funciones iterativas (usando un p. Ej.Stack objeto, que se rellena en el montón en lugar de en la pila de tiempo de ejecución). Para este particularfact función, es bastante fácil convertirlo. Mi versión iterativa se vería así:

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));
  }
}

Para su información, como lo muestra la solución iterativa anterior, elfact La función no puede calcular el factorial exacto de los números por encima de 65 (en realidad, incluso por encima de 20), porque el tipo incorporado Javalong se desbordaría. Refactorizaciónfact entonces devolvería unBigInteger en lugar delong produciría resultados exactos para entradas grandes también.

Respuestas a la pregunta(9)

Su respuesta a la pregunta