Как обращаться с StackOverflowError в Java?
Как обрабатывать StackOverflowError
в Java?
Ответы
Ответ 1
Я не уверен, что вы имеете в виду с "handle".
Вы можете наверняка поймать эту ошибку:
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...");
}
}
но это, скорее всего, плохая идея, если вы точно не знаете, что делаете.
Ответ 2
Вероятно, у вас есть бесконечная рекурсия.
т.е. метод, который вызывает себя снова и снова
public void sillyMethod()
{
sillyMethod();
}
Один из них - это исправить ваш код, чтобы рекурсия прекращалась, а не продолжалась навсегда.
Ответ 3
Взгляните на Raymond Chen post При отладке, вы хотите сосредоточиться на повторяющейся рекурсивной части. Выдержка:
Если вы отправитесь на поиски через вашу базу данных отслеживания дефектов, пытаясь выяснить, является ли это известной проблемой или нет, поиск верхних функций в стеке вряд ли найдет что-нибудь интересное. Это связано с тем, что переполнение стека происходит в случайной точке рекурсии; каждый переполнение стека выглядит внешне отличным от любого другого, даже если они являются одним и тем же переполнением стека.
Предположим, вы поете песню Frère Jacques, за исключением того, что вы поете каждый стих на несколько тонов выше, чем предыдущий. В конце концов, вы достигнете вершины своего диапазона пения, и именно там, где это происходит, зависит от того, где ваш вокальный предел выравнивается против мелодии. В мелодии первые три ноты - это новый "рекордный максимум" (т.е. Ноты выше, чем любая другая нота, исполняемая до сих пор), и новые рекордные максимумы появляются в трех нотах третьего измерения, а финальная запись высоко во второй ноте пятой меры.
Если мелодия представляет собой использование стека программ, переполнение стека может произойти в любом из этих пяти местоположений при выполнении программы. Другими словами, одна и та же базовая рекурсия убегания (музыкально представленная все более высоким исполнением мелодии) может проявляться пятью различными способами. "Рекурсия" в этой аналогии была довольно быстрой, всего восемь баров до повторения цикла. В реальной жизни цикл может быть довольно длинным, что приводит к десяткам потенциальных точек, в которых может проявляться переполнение стека.
Если вы столкнулись с переполнением стека, вы хотите игнорировать верхнюю часть стека, поскольку это просто фокусируется на конкретной заметке, которая превысила ваш диапазон вокала. Вы действительно хотите найти всю мелодию, так как то, что является общим для всех стеков, переполняется одной и той же первопричиной.
Ответ 4
Возможно, вам захочется узнать, поддерживается ли опция "-Xss" вашей JVM. Если это так, вы можете попробовать установить его на значение 512k (по умолчанию 256k под 32-разрядными Windows и Unix) и посмотреть, не делает ли это что-либо (кроме того, что вы сидите дольше, чем ваше исключение StackOverflowException). Обратите внимание, что это параметр для потока, поэтому, если у вас много потоков, вы также можете настроить настройки кучи.
Ответ 5
Правильный ответ - тот, который уже дан. Вероятно, вы либо a) имеете ошибку в своем коде, что приводит к бесконечной рекурсии, которую обычно довольно легко диагностировать и исправлять, или b) иметь код, который может привести к очень глубоким рекурсиям, например, рекурсивному перемещению несбалансированного двоичного дерева. В последней ситуации вам нужно изменить свой код, чтобы не выделять информацию в стеке (то есть не рекурсивно), а вместо этого выделять его в кучу.
Например, для несбалансированного обхода дерева вы можете сохранить узлы, которые нужно будет пересмотреть в структуре данных стека. Для обхода вы должны пересечь левые ветки, нажимая каждый node по мере того, как вы посещали его, пока не нажмете лист, который вы обработаете, затем вытащите node с верхней части стека, обработайте его, а затем перезапустите ваш цикл с правильным дочерним элементом (просто установив переменную цикла вправо node.) Это будет использовать постоянное количество стека, перемещая все, что было в стек, в кучу в структуре данных Stack. Куча обычно намного больше, чем стек.
Как что-то, что обычно является крайне плохой идеей, но необходимо в случаях, когда использование памяти крайне ограничено, вы можете использовать разворот указателя. В этом методе вы кодируете стек в структуру, в которую вы проходите, и повторно используя ссылки, которые вы просматриваете, вы можете сделать это без какой-либо или значительно меньшей дополнительной памяти. Используя вышеприведенный пример, вместо того, чтобы толкать узлы, когда мы зацикливаем, нам просто нужно запомнить нашего непосредственного родителя, и на каждой итерации мы установили ссылку, которую мы перешли к текущему родительскому элементу, а затем текущий родительский элемент node, который мы оставляем, Когда мы добираемся до листа, мы обрабатываем его, затем идем к нашему родителю, а потом у нас головоломка. Мы не знаем, исправить ли левую ветвь, обработать этот node и продолжить с правой ветвью, или исправить правильную ветвь и перейти к нашему родителю. Поэтому нам нужно выделить дополнительный бит информации по мере повторения. Как правило, для низкоуровневых реализаций этого метода этот бит будет храниться в самом указателе, что приведет к отсутствию дополнительной памяти и постоянной памяти в целом. Это не вариант в Java, но может быть возможно отбросить этот бит в полях, используемых для других вещей. В худшем случае это по меньшей мере 32 или 64 раза меньше необходимого объема памяти. Конечно, этот алгоритм чрезвычайно легко ошибаться с совершенно запутывающими результатами и может вызвать полный хаос с помощью concurrency. Поэтому почти никогда не стоит кошмар, за исключением случаев, когда выделение памяти несостоятельно. Типичным примером является сборщик мусора, где такие алгоритмы являются общими.
Я действительно хотел поговорить о том, когда вы, возможно, захотите обработать StackOverflowError. А именно для обеспечения устранения хвостового вызова на JVM. Один из подходов заключается в использовании стиля батута, в котором вместо выполнения хвостового вызова вы возвращаете объект с нулевой процедурой или если вы просто возвращаете значение, которое вы возвращаете. [Примечание: для этого требуется некоторое средство выражения функции, возвращающей либо A, либо B. В Java, возможно, самый легкий способ сделать это - вернуть один тип в обычном режиме и выбросить другое как исключение.] Затем, когда вы вызываете метод, вы необходимо выполнить цикл while, вызывающий нулевые процедуры (которые сами возвратят либо нулевую процедуру, либо значение), пока не получите значение. Бесконечный цикл станет циклом while, который постоянно заставляет объекты процедуры возвращать объекты процедуры. Преимущества батут-стиля заключаются в том, что он использует только постоянный фактор большего количества стека, чем вы использовали бы с реализацией, которая должным образом устраняет все хвостовые вызовы, использует обычный стек Java для не-хвостовых вызовов, простота перевода и только увеличивает код (утомительный) постоянный коэффициент. Недостатком является то, что вы выделяете объект при каждом вызове метода (который сразу же становится мусором), и использование этих объектов связано с несколькими косвенными вызовами на хвостовой вызов.
Идеальная вещь - никогда не выделять эти нулевые процедуры или что-то еще в первую очередь, что именно то, что будет устранено устранением хвоста. Однако, работая с тем, что предоставляет Java, мы могли бы запустить код как обычно и делать только эти нулевые процедуры, когда у нас заканчивается стек. Теперь мы по-прежнему выделяем эти бесполезные фреймы, но делаем это на стеке, а не на кучу, и освобождаем их навалом, а также наши вызовы - обычные прямые вызовы Java. Самый простой способ описать это преобразование - сначала переписать все методы с несколькими вызовами в методы, которые имеют два оператора вызова, то есть fgh() {f(); г(); час(); } становится fgh() {f(); GH(); } и gh() {g(); час(); }. Для простоты я предполагаю, что все методы заканчиваются хвостовым вызовом, которые могут быть организованы путем простой упаковки остальной части метода в отдельный метод, хотя на практике вы бы хотели обработать их напрямую. После этих преобразований мы имеем три случая: либо метод имеет нулевые вызовы, и в этом случае делать нечего, либо он имеет один (хвостовой) вызов, и в этом случае мы обертываем его в блок try-catch тем же, что и для хвостовой вызов в двух случаях вызова. Наконец, он может иметь два вызова, не хвостовой вызов и хвостовой вызов, и в этом случае мы применяем следующее преобразование, проиллюстрированное на примере (используя нотацию С# лямбда, которую можно легко заменить анонимным внутренним классом с некоторым ростом):
// 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 и восстанавливают их с добавленным оставшимся кодом. Это разбудит стек вплоть до верхнего уровня, исключая фреймы обратных вызовов, но помня о не-хвостовых кадрах вызовов в нулевой процедуре. Когда мы, наконец, выполним процедуру в исключении Bounce на верхнем уровне, мы воссоздаем все ненулевые кадры вызова. На этом этапе, если мы снова выйдем из стека, тогда, поскольку мы не переустанавливаем обработчики StackOverflowError, он будет исчезать по желанию, поскольку мы действительно не в стеке. Если мы немного поработаем, новый StackOverflowError будет установлен соответствующим образом. Более того, если мы действительно достигнем прогресса, но потом снова закончим стеки, нет никакой выгоды, чтобы повторно размотать рамы, которые мы уже размотали, поэтому мы устанавливаем новые обработчики верхнего уровня, чтобы стек был только раздутым до них.
Самая большая проблема с этим подходом заключается в том, что вы, вероятно, захотите вызвать обычные методы Java, и вы можете иметь сколь угодно мало пространства стека, когда вы это сделаете, поэтому у них может быть достаточно места для запуска, но не закончено, и вы не можете возобновить они посередине. Для этого есть как минимум два решения. Первый - отправить всю эту работу в отдельный поток, который будет иметь собственный стек. Это довольно эффективно и довольно легко и не будет вводить никаких concurrency (если вы этого не хотите). Еще один вариант - просто специально раскрутить стек перед вызовом любого обычного метода Java, просто выбросив StackOverflowError непосредственно перед ними. Если при повторном завершении все еще заканчивается пространство стека, вы сначала должны были прикручиваться.
Аналогичная вещь может быть сделана и для продолжения точно в срок. К сожалению, это преобразование не очень удобно выполнять вручную в Java и, вероятно, является границей для таких языков, как С# или Scala. Таким образом, такие преобразования, как правило, выполняются языками, ориентированными на JVM, а не людьми.
Ответ 6
Я думаю, вы не можете - или это, по крайней мере, зависит от используемого вами jvm. Переполнение стека означает, что у вас нет места для хранения локальных переменных и возврата адресов. Если ваш jvm выполняет некоторую форму компиляции, у вас есть stackoverflow в jvm, а это значит, вы не можете справиться с этим или поймать его. Jvm должен завершиться.
Может существовать способ создания jvm, который допускает такое поведение, но он будет медленным.
Я не тестировал поведение с jvm, но в .net вы просто не можете обрабатывать stackoverflow. Даже попробовать поймать не поможет. Поскольку java и .net полагаются на одну и ту же концепцию (виртуальные машины с jit), я подозреваю, что Java будет вести себя одинаково. Присутствие исключения stackoverflow в .NET предполагает, что может быть некоторое vm, которое действительно позволяет программе поймать его, нормальное не работает.
Ответ 7
Большинство шансов получить StackOverflowError
- используя рекурсии [long/end] в рекурсивных функциях.
Вы можете избежать рекурсии функций, изменив дизайн приложения, чтобы использовать объекты данных с возможностью стекирования. Существуют схемы кодирования для преобразования рекурсивных кодов в итеративные кодовые блоки. Посмотрите ниже ответы:
Таким образом, вы избегаете стекирования памяти Java для своих рецессивных вызовов функций, используя свои собственные стеки данных.
Ответ 8
Трассировка стека должна указывать на характер проблемы. При чтении трассировки стека должно быть очевидное зацикливание.
Если это не ошибка, вам нужно добавить счетчик или какой-либо другой механизм, чтобы остановить рекурсию до того, как рекурсия будет настолько глубокой, что это приведет к переполнению стека.
Примером этого может быть, если вы обрабатываете вложенный XML-код в DOM-модели с рекурсивными вызовами, а XML вложен настолько глубоко, что вызывает переполнение стека с вашими вложенными вызовами (маловероятно, но возможно). Это должно быть довольно глубокое вложение, чтобы вызвать переполнение стека.
Ответ 9
Как упоминалось многими в этом потоке, общей причиной этого является вызов рекурсивного метода, который не завершается. По возможности избегайте, и если вы это испытываете, вы должны считать это в большинстве случаев серьезной ошибкой. В некоторых случаях вы можете настроить размер стека потоков в Java на большее, чтобы справляться с некоторыми обстоятельствами (большие массивы данных управляются в локальном хранилище стека, длинные рекурсивные вызовы), но это увеличит общий объем памяти, что может привести к возникновению проблем с числом потоков, доступных в VM. Как правило, если вы получаете это исключение, поток и любые локальные данные в этот поток должны считаться тостами и не использоваться (т.е. Подозреваемые и, возможно, испорченные).
Ответ 10
Простой,
Посмотрите на трассировку стека, которую создает StackOverflowError, чтобы вы знали, где находится ваш код, и используйте его, чтобы выяснить, как переписать код, чтобы он не вызывал себя рекурсивно (вероятная причина вашей ошибки), поэтому это не повторится.
StackOverflowErrors - это не то, что нужно обрабатывать с помощью предложения try... catch, но оно указывает на основной недостаток в логике вашего кода, который должен быть исправлен вами.
Ответ 11
java.lang.Error javadoc:
Ошибка - это подкласс Throwable , который указывает на серьезные проблемы, которые разумное приложение не должно пытаться поймать. Большинство таких ошибок являются ненормальными условиями. Ошибка ThreadDeath, хотя и является "нормальным" условием, также является подклассом Error, поскольку большинство приложений не должны пытаться ее поймать.
Метод не обязан объявлять в свойстве throws любые подклассы Error, которые могут быть выбраны во время выполнения метода, но не пойманы, поскольку эти ошибки являются ненормальными условиями, которые никогда не должны возникать.
Итак, не надо. Попытайтесь найти то, что неправильно в логике вашего кода. Это исключение происходит очень часто из-за бесконечной рекурсии.
Ответ 12
в какой-то момент вы не можете поймать stackoverflowerror. всякий раз, когда вы пытаетесь, вы столкнетесь с новым. потому что это java vm. хорошо найти рекурсивные кодовые блоки, например Андрей Буллок сказал.
Ответ 13
/*
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 на
// понимаем программные snipets, которые могут вызвать ошибку