Вопрос

Как обращаться StackOverflowError на Java ?

Это было полезно?

Решение

Я не уверен, что вы подразумеваете под словом "обрабатывать".

Вы, конечно, можете уловить эту ошибку:

public class Example {
    public static void endless() {
        endless();
    }

    public static void main(String args[]) {
        try {
            endless();
        } catch(StackOverflowError t) {
            // more general: catch(Error t)
            // anything: catch(Throwable t)
            System.out.println("Caught "+t);
            t.printStackTrace();
        }
        System.out.println("After the error...");
    }
}

но это, скорее всего, плохая идея, если только вы точно не знаете, что делаете.

Другие советы

Вероятно, у вас происходит какая-то бесконечная рекурсия.

То есть.метод , который вызывает себя снова и снова

public void sillyMethod()
{
    sillyMethod();
}

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

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

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

Предположим, вы поете песню Frère Jacques, за исключением того, что вы поете каждый куплет на несколько тонов выше, чем предыдущий.В конце концов, вы достигнете вершины своего певческого диапазона, и когда именно это произойдет, зависит от того, насколько ваш вокальный предел соответствует мелодии.В мелодии каждая из первых трех нот является новым "рекордно высоким" (т. е. ноты выше, чем любая другая нота, спетая до сих пор), и новые рекордные максимумы появляются в трех нотах третьего такта, а окончательный рекордный максимум - во второй ноте пятого такта.

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

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

Возможно, вы захотите узнать, поддерживается ли опция "-Xss" вашей JVM.Если это так, вы можете попробовать установить для него значение 512k (по умолчанию 256k в 32-разрядных Windows и Unix) и посмотреть, поможет ли это что-нибудь (кроме того, что вы заставите вас сидеть дольше до появления исключения StackOverflowException).Обратите внимание, что это настройка для каждого потока, поэтому, если у вас запущено много потоков, вы также можете захотеть увеличить настройки кучи.

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

Например, для несбалансированного обхода дерева вы могли бы сохранить узлы, к которым потребуется вернуться, в структуре данных стека.Для обхода по порядку вы бы перебирали левые ветви, нажимая на каждый узел по мере его посещения, пока не наткнетесь на лист, который вы бы обработали, затем извлекли узел из верхней части стека, обработали его, затем перезапустили свой цикл с правильным дочерним элементом (просто установив переменную цикла в правильный узел).) При этом будет использоваться постоянный объем стека путем перемещения всего, что было в стеке, в кучу в структуре данных стека.Куча, как правило, гораздо более многочисленна, чем стек.

Как правило, это крайне плохая идея, но необходимая в случаях, когда использование памяти крайне ограничено, вы можете использовать обращение указателя.В этом методе вы кодируете стек в структуру, которую вы просматриваете, и, повторно используя ссылки, которые вы просматриваете, вы можете сделать это без дополнительной памяти или со значительно меньшим ее объемом.Используя приведенный выше пример, вместо того, чтобы выталкивать узлы при выполнении цикла, нам просто нужно запомнить нашего непосредственного родителя, и на каждой итерации мы устанавливаем пройденную ссылку на текущего родителя, а затем текущего родителя на узел, который мы покидаем.Когда мы добираемся до листа, мы обрабатываем его, затем переходим к нашему родителю, и тогда у нас возникает головоломка.Мы не знаем, исправлять ли левую ветвь, обрабатывать этот узел и продолжать с правой ветвью, или исправить правую ветвь и перейти к нашему родителю.Поэтому нам нужно выделять дополнительный бит информации по мере выполнения итерации.Как правило, для низкоуровневых реализаций этого метода этот бит будет сохранен в самом указателе, что приведет к отсутствию дополнительной памяти и постоянной памяти в целом.В Java это невозможно, но, возможно, удастся скрыть этот бит в полях, используемых для других целей.В худшем случае это все равно приведет к уменьшению объема необходимой памяти как минимум в 32 или 64 раза.Конечно, этот алгоритм чрезвычайно легко ошибиться, что приведет к совершенно запутанным результатам и приведет к полному хаосу с параллелизмом.Так что это почти никогда не стоит кошмара обслуживания, за исключением случаев, когда выделение памяти невозможно.Типичным примером является сборщик мусора, где подобные алгоритмы распространены.

Однако о чем я действительно хотел поговорить, так это о том, когда вы, возможно, захотите обработать StackOverflowError .А именно, чтобы обеспечить устранение хвостового вызова в JVM.Один из подходов заключается в использовании стиля trampoline, где вместо выполнения конечного вызова вы возвращаете нулевой объект процедуры, или, если вы просто возвращаете значение, вы возвращаете это.[Примечание:это требует некоторого способа сказать, что функция возвращает либо A , либо B.В Java, вероятно, самый простой способ сделать это - вернуть один тип обычным способом и выдать другой в качестве исключения.] Затем всякий раз, когда вы вызываете метод, вам нужно выполнять цикл while, вызывающий нулевые процедуры (которые сами будут возвращать либо нулевую процедуру, либо значение), пока вы не получите значение.Бесконечный цикл превратится в цикл while, который постоянно вызывает объекты процедуры, возвращающие объекты процедуры.Преимущества стиля trampoline в том, что он использует только на постоянный коэффициент больше стека, чем вы использовали бы при реализации, которая должным образом устранила все хвостовые вызовы, он использует обычный Java-стек для не-хвостовых вызовов, перевод простой, и он увеличивает код только на (утомительный) постоянный коэффициент.Недостатком является то, что вы выделяете объект при каждом вызове метода (который немедленно становится мусором), и использование этих объектов включает в себя пару косвенных вызовов для каждого конечного вызова.

Идеальным решением было бы вообще никогда не выделять эти нулевые процедуры или что-либо еще, что является именно тем, чего достигло бы устранение хвостового вызова.Однако, работая с тем, что предоставляет Java, мы могли бы выполнить код в обычном режиме и выполнять эти нулевые процедуры только тогда, когда у нас заканчивается стек.Теперь мы по-прежнему выделяем эти бесполезные фреймы, но мы делаем это в стеке, а не в куче, и освобождаем их массово, кроме того, наши вызовы являются обычными прямыми вызовами Java.Самый простой способ описать это преобразование - сначала переписать все методы с несколькими операторами вызова в методы, которые имеют два оператора вызова, т. е.fgh() { f();g();h();} становится fgh() { f();gh();} и gh(){ g();h();}.Для простоты я предположу, что все методы заканчиваются конечным вызовом, который можно организовать, просто упаковав оставшуюся часть метода в отдельный метод, хотя на практике вы хотели бы обрабатывать их напрямую.После этих преобразований у нас есть три случая: либо метод имеет нулевые вызовы, и в этом случае нечего делать, либо у него есть один (хвостовой) вызов, и в этом случае мы оборачиваем его в блок try-catch тем же самым, что мы сделаем для хвостового вызова в случае двух вызовов.Наконец, у него может быть два вызова, не завершающий вызов и конечный вызов, и в этом случае мы применяем следующее преобразование, проиллюстрированное примером (используя лямбда-нотацию C #, которую можно легко заменить анонимным внутренним классом с некоторым ростом):

// top-level handler
Action tlh(Action act) {
    return () => {
        while(true) {
            try { act(); break; } catch(Bounce e) { tlh(() => e.run())(); }
        }
    }
}

gh() {
    try { g(); } catch(Bounce e) { 
        throw new Bounce(tlh(() => { 
            e.run(); 
            try { h(); } catch(StackOverflowError e) {
                throw new Bounce(tlh(() => h());
            }
        }); 
    }
    try { h(); } catch(StackOverflowError e) { 
        throw new Bounce(tlh(() => h())); 
    }
}

Главное преимущество здесь в том, что если исключение не генерируется, это тот же код, с которого мы начинали, только с установленными некоторыми дополнительными обработчиками исключений.Поскольку хвостовые вызовы (вызов h()) не обрабатывают исключение Bounce, это исключение будет проходить через них, разматывая эти (ненужные) кадры из стека.Вызовы без хвоста перехватывают исключения отказов и повторно загружают их с добавлением оставшегося кода.Это позволит размотать стек до самого верхнего уровня, исключив фреймы хвостового вызова, но запомнив фреймы не-хвостового вызова в нулевой процедуре.Когда мы, наконец, выполним процедуру в исключении Bounce на верхнем уровне, мы воссоздадим все фреймы вызова, не являющиеся конечными.На этом этапе, если у нас немедленно снова закончится stack, то, поскольку мы не переустанавливаем обработчики StackOverflowError, он не будет перехвачен по желанию, поскольку у нас действительно закончился stack.Если мы продвинемся немного дальше, новый StackOverflowError будет установлен соответствующим образом.Более того, если мы добьемся прогресса, но затем снова исчерпаем стек, нет никакой пользы от повторного разматывания фреймов, которые мы уже размотали, поэтому мы устанавливаем новые обработчики верхнего уровня, так что стек будет разматываться только до них.

Самая большая проблема с этим подходом заключается в том, что вы, вероятно, захотите вызвать обычные методы Java, и у вас может быть сколь угодно мало места в стеке, когда вы это сделаете, поэтому у них может быть достаточно места для запуска, но не завершения, и вы не сможете возобновить их в середине.Есть по крайней мере два решения этой проблемы.Первый - отправить всю такую работу в отдельный поток, который будет иметь свой собственный стек.Это довольно эффективно и довольно просто и не приведет к какому-либо параллелизму (если вы этого не хотите). Другой вариант - просто намеренно размотать стек перед вызовом любого обычного метода Java, просто бросив StackOverflowError непосредственно перед ними.Если при возобновлении работы в нем по-прежнему не хватает места в стеке, значит, вы с самого начала облажались.

Аналогичную вещь можно сделать и для того, чтобы делать продолжения точно в срок.К сожалению, это преобразование на самом деле невозможно выполнить вручную в Java и, вероятно, является пограничным для таких языков, как C # или Scala.Таким образом, подобные преобразования, как правило, выполняются языками, нацеленными на JVM, а не людьми.

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

Мог бы быть способ создать jvm, который допускал бы такое поведение, но это было бы медленно.

Я не тестировал поведение с jvm, но в .net вы просто не можете обрабатывать stackoverflow.Даже попытка поймать не поможет.Поскольку java и .net полагаются на одну и ту же концепцию (виртуальные машины с jit) Я подозреваю, что java будет вести себя так же.Наличие исключения stackoverflow в .NET предполагает, что может существовать какая-то виртуальнаямашина, которая позволяет программе перехватывать его, хотя обычная этого не делает.

Наибольшие шансы получить StackOverflowError являются путем использования [длинных / бесконечных] рекурсий в рекурсивных функциях.

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

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

Трассировка стека должна указывать на характер проблемы.При чтении трассировки стека должно наблюдаться некоторое очевидное зацикливание.

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

Примером этого может быть, если вы обрабатываете вложенный XML в модели DOM с рекурсивными вызовами, и XML вложен настолько глубоко, что вызывает переполнение стека вашими вложенными вызовами (маловероятно, но возможно).Однако это должно было бы быть довольно глубокой вложенностью, чтобы вызвать переполнение стека.

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

Простой,

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

Ошибки StackOverflowErrors - это не то, что нужно обрабатывать с помощью предложения try ... catch, но это указывает на основной недостаток в логике вашего кода, который необходимо исправить вам.

java.lang.Ошибка javadoc:

Ошибка - это подкласс Throwable это указывает на серьезные проблемы, которые разумное приложение не должно пытаться выявить.Большинство таких ошибок являются ненормальными условиями.Ошибка ThreadDeath, хотя и является "нормальным" состоянием, также является подклассом ошибок, поскольку большинство приложений не должны пытаться ее перехватить.Метод не обязан объявлять в своем предложении throws какие-либо подклассы ошибок, которые могут быть вызваны во время выполнения метода, но не перехвачены, поскольку эти ошибки являются ненормальными условиями, которые никогда не должны возникать.

Так что не надо.Попытайтесь найти, что не так в логике вашего кода.Это исключение встречается очень часто из-за бесконечной рекурсии.

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

/*
Using Throwable we can trap any know error in JAVA..
*/
public class TestRecur {
    private int i = 0;


    public static void main(String[] args) {
        try {
            new TestRecur().show();
        } catch (Throwable err) {
            System.err.println("Error...");
        }
    }

    private void show() {
        System.out.println("I = " + i++);
        show();
    }
}

Однако вы можете взглянуть на ссылку: http://marxsoftware.blogspot.in/2009/07/diagnosing-and-resolving.html чтобы понять фрагмент кода, который может вызвать ошибку

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top