Что на самом деле вызывает ошибку переполнения стека? [дубликат]



этот вопрос уже есть ответ здесь:

  • Что такое StackOverflowError? 13 ответов

Я искал везде и не могу найти твердый ответ. Согласно документации, Java бросает java.ленг.StackOverflowError ошибка при следующих обстоятельствах:

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

но это вызывает два вопроса:

  • а разве нет других способов для переполнения стека происходит не только через рекурсию?
  • StackOverflowError происходит до того, как JVM фактически переполняет стек или после?

чтобы уточнить второй вопрос:

когда Java бросает StackOverflowError, вы можете с уверенностью предположить, что стек не сделал написать в кучу? Если вы уменьшите размер стека или кучи в try / catch на функцию, которая вызывает переполнение стека, вы можете продолжить работу? Это где-нибудь задокументировано?

ответы, которые я не ищу:

  • StackOverflow происходит из-за плохой рекурсии.
  • StackOverflow происходит, когда куча встречается со стеком.
598   10  

10 ответов:

кажется, вы думаете, что a ошибка stackoverflow похоже на исключение переполнения буфера в собственных программах, когда существует риск записи в память, которая не была выделена для буфера, и, таким образом, повредить некоторые другие места памяти. Это не так.

JVM имеет заданную память, выделенную для каждого стека каждого потока, и если попытка вызвать метод происходит, чтобы заполнить эту память, JVM выдает ошибку. Как это сделать, если вы были попытка записи по индексу N массива длины N. никакого повреждения памяти произойти не может. Стек не может писать в кучу.

StackOverflowError для стека-это то же, что OutOfMemoryError для кучи: он просто сигнализирует, что больше нет доступной памяти.

описание ошибок виртуальной машины (§6.3)

StackOverflowError: реализация виртуальной машины Java исчерпала пространство стека для a поток, как правило, потому, что поток выполняет неограниченное количество рекурсивных вызовов в результате ошибки в выполняющейся программе.

а разве нет других способов для переполнения стека происходит не только через рекурсию?

конечно. Просто продолжайте вызывать методы, никогда не возвращаясь. Вам понадобится много методов, если вы не позволите рекурсии. На самом деле, это не имеет значения: кадр стека-это кадр стека, независимо от того, является ли он одним из рекурсивных методов или нет.

ответ на ваш второй вопрос: stackoverflow обнаруживается, когда JVM пытается выделите кадр стека для следующего вызова, и находит это невозможно. Таким образом, ничего не будет перезаписано.

нет ли других способов для переполнения стека, не только через рекурсию?

Вызов принят :) StackOverflowErrorбез рекурсии (вызов не удалось, см. комментарии):

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

компиляция со стандартом javac Test.java и запускать с java -Xss104k Test 2> out. После этого, more out скажу вам:

Exception in thread "main" java.lang.StackOverflowError

вторая попытка.

теперь идея еще проще. Примитивы в Java могут храниться на стек. Итак, давайте объявим много двойников, как double a1,a2,a3.... Этот скрипт может писать, компилировать и запускать код о нас:

#!/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

и... Я получил что-то неожиданное:

#
# 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

это 100% повторяется. Это связано с вашим вторым вопросом:

StackOverflowError происходит до того, как JVM фактически переполняется стек или после?

Итак, в случае OpenJDK 20.0-b12 мы можем видеть, что JVM во-первых взорванный. Но это похоже на ошибку, может быть, кто-то может подтвердить это в комментариях, пожалуйста, потому что я не уверен. Должен ли я сообщить об этом? Может быть, это уже исправлено в какой-то новой версии... Согласно ссылка на спецификацию JVM (дан JB Nizet в комментарии) JVM должен бросить StackOverflowError, не умирают:

если вычисление в потоке требует большей виртуальной машины Java стек, чем разрешено, виртуальная машина Java бросает StackOverflowError.


третья попытка.

public class Test {
    Test test = new Test();

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

мы хотим создать новый

наиболее распространенной причиной StackOverFlowError является чрезмерно глубокая или бесконечная рекурсия.

например:

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

В Java:

здесь two области в памяти куча и стек. Элемент stack memory используется для хранения локальных переменных и вызов функции, в то время как heap memory используется для хранения объектов в Java.

если в стеке не осталось памяти для хранения вызова функции или локальной переменной, JVM бросит java.lang.StackOverFlowError

в то время как если там нет больше места кучи для создания объекта, JVM будет бросать java.lang.OutOfMemoryError

нет никакого "StackOverFlowException". Вы имеете в виду"StackOverFlowError".

Да, вы можете продолжать работать, если поймаете его, потому что стек очищается, когда вы это делаете, но это будет плохой и уродливый вариант.

когда именно возникает ошибка ? - Когда вы вызываете метод и JVM проверяет, достаточно ли памяти для этого. Конечно, ошибка выдается, если это невозможно.

  • нет, это единственный способ получить эту ошибку: получение ваш стек полный. Но не только через рекурсию, но и вызывая методы, которые бесконечно вызывают другие методы. Это очень специфическая ошибка, так что нет.
  • он выбрасывается до того, как стек заполнен, именно тогда, когда вы его проверяете. Куда бы вы поместили данные, если нет свободного места ? Перекрывая других ? Неа.

есть два основных места, что вещи могут храниться в Java. Первый-это куча, которая используется для динамически выделяемых объектов. new.

кроме того, каждый поток получает свой собственный стек, и он получает объем памяти, выделенной для этого стека.

при вызове метода данные помещаются в стек для записи вызова метода, передаваемых параметров и выделяемых локальных переменных. Метод с пятью локальными переменными и тремя параметры будут использовать больше пространства стека, чем a void doStuff() метод без локальных переменных.

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

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

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

StackOverflowError возникает из-за приложение рекурсивно вызывает слишком глубоко (это не ответ, который вы ожидаете).

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

но неосознанно кодируя одну строку или две строки, которые вызывают StackOverflowError понятно, и JVM бросает это, и мы можем исправить это мгновенно. здесь это мой ответ с изображением для некоторых других вопросов.

StackOverflow происходит, когда выполняется вызов функции и стек заполнен.

Так же, как ArrayOutOfBoundException. Он не может ничего испортить, на самом деле его очень легко поймать и восстановить.

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

в c# вы можете достичь переполнения стека другим способом, ошибочно определив свойства объекта. Например :

private double hours;

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

Как вы можете видеть, это навсегда будет продолжать возвращать часы с прописной буквой H, которая сама по себе будет возвращать часы и так далее.

переполнение стека часто происходит также из-за нехватки памяти или при использовании управляемых языков, потому что ваш менеджер языков (CLR, JRE) обнаружит, что ваш код застрял в бесконечном петля.

но это вызывает два вопроса:

  1. а разве нет других способов для переполнения стека происходит не только через рекурсию?
  2. StackOverflowError происходит до того, как JVM фактически переполняет стек или после?
  1. Это также может произойти, когда мы выделяем размер больше, чем предел стека (например. int x[10000000];).

  2. ответ на второй

каждый поток имеет свой собственный стек, который содержит фрейм для каждого метода, выполняющегося в этом потоке. Таким образом, текущий выполняемый метод находится в верхней части стека. Новый фрейм создается и добавляется (выталкивается) в верхнюю часть стека для каждого вызова метода. Кадр удаляется (выскакивает), когда метод возвращает нормально или если во время вызова метода возникает неперехваченное исключение. Стек не управляется напрямую, за исключением объектов push и pop frame, а также поэтому объекты фрейма могут быть выделены в куче, и память не должна быть непрерывной.

поэтому, рассматривая стек в потоке, мы можем сделать вывод.

стек может быть динамического или фиксированного размера. Если поток требует большего стека, чем разрешено StackOverflowError бросается. Если поток требует нового кадра и недостаточно памяти для его выделения, то OutOfMemoryError бросается.

вы можете получить описание для JVM здесь

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

Добавить ответ:
Отменить.