java jvm stack-overflow

java - ¿Qué causa realmente un error de desbordamiento de pila?



jvm stack-overflow (10)

¿No hay otras formas de que ocurra un desbordamiento de pila, no solo a través de la recursión?

Por supuesto. Solo sigue llamando métodos, sin volver jamás. Sin embargo, necesitarás muchos métodos, a menos que permitas la recursión. En realidad, no hace una diferencia: un marco de pila es un marco de pila, ya sea uno de un método recursivo o no es el mismo.

La respuesta a su segunda pregunta es: el stackoverflow se detecta cuando la JVM intenta asignar el marco de pila para la siguiente llamada y encuentra que no es posible. Por lo tanto, nada será sobreescrito.

Esta pregunta ya tiene una respuesta aquí:

He mirado por todas partes y no puedo encontrar una respuesta sólida. Según la documentación, Java lanza un error java.lang.StackOverflowError en las siguientes circunstancias:

Se lanza cuando se produce un desbordamiento de pila porque una aplicación recurre demasiado profundamente.

Pero esto plantea dos preguntas:

  • ¿No hay otras formas de que ocurra un desbordamiento de pila, no solo a través de la recursión?
  • ¿Ocurre el StackOverflowError antes de que la JVM realmente desborde la pila o después?

Para profundizar en la segunda pregunta:

Cuando Java lanza el StackOverflowError, ¿puede asumir con seguridad que la pila no se escribió en el montón? Si reduce el tamaño de la pila o el montón en un try / catch en una función que produce un desbordamiento de pila, ¿puede continuar trabajando? ¿Está esto documentado en alguna parte?

Respuestas que no estoy buscando:

  • Un StackOverflow ocurre debido a una mala recursión.
  • Un StackOverflow ocurre cuando el montón se encuentra con la pila.

¿No hay otras formas de que ocurra un desbordamiento de pila, no solo a través de la recursión?

Desafío aceptado :) Error sin recursión (desafío falla, ver comentarios):

public class Test { final static int CALLS = 710; public static void main(String[] args) { final Functor[] functors = new Functor[CALLS]; for (int i = 0; i < CALLS; i++) { final int finalInt = i; functors[i] = new Functor() { @Override public void fun() { System.out.print(finalInt + " "); if (finalInt != CALLS - 1) { functors[finalInt + 1].fun(); } } }; } // Let''s get ready to ruuuuuuumble! functors[0].fun(); // Sorry, couldn''t resist to not comment in such moment. } interface Functor { void fun(); } }

Compile con javac Test.java estándar y ejecute con java -Xss104k Test 2> out . Después de eso, more out le dirá:

Exception in thread "main" java.lang.Error

Segundo intento.

Ahora la idea es aún más simple. Los primitivos en Java se pueden almacenar en la pila. Entonces, vamos a declarar un montón de dobles, como double a1,a2,a3... Este script puede escribir, compilar y ejecutar el código para nosotros:

#!/bin/sh VARIABLES=4000 NAME=Test FILE=$NAME.java SOURCE="public class $NAME{public static void main(String[] args){double " for i in $(seq 1 $VARIABLES); do SOURCE=$SOURCE"a$i," done SOURCE=$SOURCE"b=0;System.out.println(b);}}" echo $SOURCE > $FILE javac $FILE java -Xss104k $NAME

Y ... tengo algo inesperado:

# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152 # # JRE version: 6.0_27-b27 # Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops) # Derivative: IcedTea6 1.12.6 # Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2 # Problematic frame: # V [libjvm.so+0x4ce501] JavaThread::last_frame()+0xa1 # # An error report file with more information is saved as: # /home/adam/Desktop/test/hs_err_pid4988.log # # If you would like to submit a bug report, please include # instructions how to reproduce the bug and visit: # https://bugs.launchpad.net/ubuntu/+source/openjdk-6/ # Aborted

Es 100% repetitivo. Esto está relacionado con su segunda pregunta:

¿Ocurre el Error antes de que la JVM realmente desborde la pila o después?

Entonces, en el caso de OpenJDK 20.0-b12 podemos ver que JVM primero explotó. Pero parece un error, tal vez alguien pueda confirmarlo en los comentarios, por favor, porque no estoy seguro. ¿Debo informar de esto? Tal vez ya esté solucionado en alguna versión más nueva ... De acuerdo con el docs.oracle.com/javase/specs/jvms/se7/html/… (dado por en un comentario) JVM debería lanzar un Error , no morir:

Si el cálculo en un subproceso requiere una pila de la Máquina Virtual de Java más grande que la permitida, la Máquina Virtual de Java lanza un Error.

Tercer intento

public class Test { Test test = new Test(); public static void main(String[] args) { new Test(); } }

Queremos crear un nuevo objeto de Test . Así, se llamará a su constructor (implícito). Pero, justo antes de eso, se inicializan todos los miembros de Test . Entonces, Test test = new Test() se ejecuta primero ...

Queremos crear un nuevo objeto de Test ...

Actualización: mala suerte, esto es recursión, hice una pregunta sobre eso here .


Pero esto plantea dos preguntas:

  1. ¿No hay otras formas de que ocurra un desbordamiento de pila, no solo a través de la recursión?
  2. ¿Ocurre el Error antes de que la JVM realmente desborde la pila o después?
  1. También puede ocurrir cuando estamos asignando un tamaño mayor que el límite de la pila (por ejemplo, int x[10000000]; ).

  2. La respuesta al segundo es

Cada hilo tiene su propia pila que contiene un marco para cada método que se ejecuta en ese hilo. Así que el método que se está ejecutando actualmente está en la parte superior de la pila. Se crea un nuevo marco y se agrega (empuja) a la parte superior de la pila para cada invocación de método. El marco se elimina (aparece) cuando el método vuelve normalmente o si se lanza una excepción no detectada durante la invocación del método. La pila no se manipula directamente, excepto para empujar y abrir objetos de marco, y por lo tanto los objetos de marco pueden asignarse en el Heap y la memoria no necesita ser contigua.

Así que considerando la pila en un hilo podemos concluir.

Una pila puede ser de un tamaño dinámico o fijo. Si un hilo requiere una pila más grande que la permitida, se lanza un Error . Si un hilo requiere un nuevo marco y no hay suficiente memoria para asignarlo, se OutOfMemoryError un OutOfMemoryError .

Puedes obtener la descripción de JVM here


En c # puede lograr el desbordamiento de pila de una manera diferente, definiendo erróneamente las propiedades del objeto. Por ejemplo :

private double hours; public double Hours { get { return Hours; } set { Hours = value; } }

Como puede ver, esto siempre seguirá regresando a las Horas con una H mayúscula, que en sí misma devolverá las Horas y así sucesivamente.

A menudo, también se producirá un desbordamiento de pila debido a la falta de memoria o al usar idiomas administrados porque su administrador de idiomas (CLR, JRE) detectará que su código se atascó en un bucle infinito.


Hay dos lugares principales donde las cosas se pueden almacenar en Java. El primero es el montón, que se utiliza para objetos asignados dinámicamente. new

Además, cada subproceso en ejecución tiene su propia pila y obtiene una cantidad de memoria asignada a esa pila.

Cuando llama a un método, los datos se insertan en la pila para registrar la llamada del método, los parámetros que se pasan y las variables locales que se asignan. Un método con cinco variables locales y tres parámetros usará más espacio de pila que un método void doStuff() sin variables locales.

Las principales ventajas de la pila son que no hay fragmentación de la memoria, todo para una llamada de método se asigna en la parte superior de la pila, y que regresar de los métodos es fácil. Para regresar de un método, solo tiene que desenrollar la pila al método anterior, establezca cualquier valor necesario para el valor de retorno y listo.

Debido a que la pila es un tamaño fijo por hilo, (tenga en cuenta que la especificación de Java no requiere un tamaño fijo, pero la mayoría de las implementaciones de JVM en el momento de la escritura utilizan un tamaño fijo) y porque el espacio en la pila es necesario siempre que realice un método. esperemos que ahora debería quedar claro por qué puede agotarse y qué puede hacer que se agote. No hay un número fijo de llamadas a métodos, no hay nada específico acerca de la recursión, obtienes la excepción a la que intentas llamar método y no hay suficiente memoria.

Por supuesto, el tamaño de las pilas se establece lo suficientemente alto como para que sea muy poco probable que ocurra en el código regular. Sin embargo, en el código recursivo puede ser bastante fácil realizar recursiones a grandes profundidades, y en ese momento empiezas a encontrarte con este error.


La causa más común de Error es una recursión excesivamente profunda o infinita.

Por ejemplo:

public int yourMethod(){ yourMethod();//infinite recursion }

En Java:

Hay two áreas en la memoria el montón y la pila. La stack memory se utiliza para almacenar variables locales y llamadas de función, mientras que la heap memory se utiliza para almacenar objetos en Java.

Si no queda memoria en la pila para almacenar la llamada a la función o la variable local, JVM lanzará java.lang.Error

mientras que si no hay más espacio de almacenamiento dinámico para crear objetos, JVM lanzará java.lang.OutOfMemoryError


No hay "Exception". Lo que quiere decir es "Error".

Sí, puedes continuar trabajando si lo atrapas porque la pila se borra cuando haces eso, pero esa sería una opción mala y fea.

¿Cuándo se produce exactamente el error? - Cuando llama a un método y la JVM verifica si hay suficiente memoria para hacerlo. Por supuesto, el error se produce si no es posible.

  • No, esa es la única forma en que puede obtener ese error: llenando su pila. Pero no solo a través de la recursión, también llamando a métodos que llaman infinitamente a otros métodos. Es un error muy específico por lo que no.
  • Se lanza antes de que la pila esté llena, exactamente cuando la verificas. ¿Dónde pondrías los datos si no hay espacio disponible? ¿Anulando a los demás? Naah

Parece que está pensando que un java.lang.Error es como una excepción de desbordamiento de búfer en programas nativos, cuando existe el riesgo de escribir en la memoria que no se asignó para el búfer y, por lo tanto, de dañar otras ubicaciones de memoria. No es el caso en absoluto.

JVM tiene una memoria dada asignada para cada pila de cada hilo, y si un intento de llamar a un método sucede para llenar esta memoria, JVM arroja un error. Al igual que lo haría si estuviera tratando de escribir en el índice N de una matriz de longitud N. No se pueden producir daños en la memoria. La pila no puede escribir en el montón.

Un Error es para la pila lo que un OutOfMemoryError es para el montón: simplemente indica que no hay más memoria disponible.

Descripción de los errores de la máquina virtual (§6.3)

Error : la implementación de Java Virtual Machine se ha quedado sin espacio de pila para un subproceso, normalmente porque el subproceso está realizando un número ilimitado de invocaciones recursivas como resultado de un error en el programa en ejecución.


Un ocurre cuando se realiza una llamada de función y la pila está llena.

Al igual que una ArrayOutOfBoundException. No puede corromper nada, de hecho, es muy posible atraparlo y recuperarlo.

Por lo general, sucede como resultado de una recursión no controlada, pero también puede deberse a una llamada de pila de funciones muy profunda.


Error ocurre debido a una aplicación que se repite demasiado (esta no es una respuesta que está esperando).

Ahora, otras cosas que le sucederán a Error es seguir llamando a los métodos desde los métodos hasta que obtenga Error , pero nadie puede programar para obtener Error e incluso si esos programadores lo están haciendo, entonces no están siguiendo los estándares de codificación para la compatibilidad ciclomática que todo programador debe entender. programación. Tal razón para ''Error'' requerirá mucho tiempo para rectificarlo.

Pero sin saberlo, la codificación de una línea o dos líneas, lo que hace que Error sea ​​comprensible y JVM arroja eso, y podemos rectificarlo instantáneamente. Here está mi respuesta con foto para alguna otra pregunta.