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 maneja Java la situación donde se necesita una gran pila de tiempo de ejecución. He extendido mi pregunta con el resumen de las respuestas.¿Cómo aumentar el tamaño de la pila de Java?
Originalmente quería aumentar el tamaño de la pila JVM, por lo que los programas funcionan sin 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));
}
}
el ajuste de configuración correspondiente es la bandera java -Xss...
de línea de comandos con un valor suficientemente grande. Para el programa TT
anteriormente, funciona así con JVM de OpenJDK:
$ javac TT.java
$ java -Xss4m TT
Una de las respuestas también ha señalado que las banderas son -X...
depende 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 gran pila solo para una cadena (ver en una de las respuestas cómo). Se recomienda esto sobre java -Xss...
para evitar desperdiciar memoria para hilos que no lo necesitan.
Tenía curiosidad por lo grande que una pila del programa anterior exactamente necesita, por lo que me he encontrado que el aumento n
:
- -Xss4m puede ser suficiente para
fact(1 << 15)
- -Xss5m puede ser suficiente para
fact(1 << 17)
- -Xss7m puede ser suficiente para
fact(1 << 18)
- -Xss9m puede ser suficiente para
fact(1 << 19)
- -Xss18m puede ser en ough para
fact(1 << 20)
- -Xss35m puede ser suficiente para
fact(1 << 21)
- -Xss68m puede ser suficiente para
fact(1 << 22)
- -Xss129m puede ser suficiente para
fact(1 << 23)
- -Xss258m puede ser suficiente para
fact(1 << 24)
- -Xss515m puede ser suficiente para
fact(1 << 25)
De los números anteriores parece que Java está utilizando alrededor de 16 bytes por marco de pila para la función anterior, que es razonable.
La enumeración anterior contiene puede ser suficiente en lugar de es suficiente, ya que el requisito de pila no es determinista: se ejecutan varias veces con el mismo archivo de origen y el mismo -Xss...
veces tiene éxito y, a veces se obtiene un StackOverflowError
. P.ej. para 1 < < 20, -Xss18m
fue suficiente en 7 carreras de 10, y -Xss19m
tampoco siempre fue suficiente, pero -Xss20m
fue suficiente (en todas las 100 carreras de las 100). ¿La recolección de basura, el JIT, o algo más causa este comportamiento no determinista?
El seguimiento de pila impreso en StackOverflowError
(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 demuestra 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 buena y segura práctica de codificación considerar implementaciones alternativas, menos aptas para la pila, del mismo algoritmo. En general, es posible convertir un conjunto de funciones recursivas en funciones iterativas (utilizando un objeto por ejemplo Stack
, que se rellena en el montón en lugar de en la pila de tiempo de ejecución). Para esta función particular fact
, es bastante fácil de convertir. 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));
}
}
su información, como la solución iterativa anterior muestra que, la función fact
no puede calcular el factorial exacta de los números por encima de 65 (en realidad, incluso por encima de 20), debido a que el Java incorporado tipo long
se desbordará. Refactorizar fact
para que devuelva BigInteger
en lugar de long
también daría resultados exactos para grandes entradas.
Parece más simple de lo que es. hecho() se llama 32K veces recursivamente. Eso debería ser menos de 1MB de stack. : -/ –
@Aaron: + Función de sobrecarga, que es .. a MUCHO – halfdan
Además de los problemas de tu pila. tenga en cuenta que está volando su largo y enteros. 1 << 4 es el valor máximo que puedo usar antes de pasar a negativo y luego a 0. Intente utilizar BigInteger – Sean