Що може спричинити java.lang.StackOverflowError
? Роздрук стека, який я отримую, зовсім не дуже глибокий (лише 5 методів).
Відповіді:
Перевірте наявність рекусивних викликів методів. В основному це викликається, коли існує рекурсивний виклик методу. Простий приклад
public static void main(String... args) {
Main main = new Main();
main.testMethod(1);
}
public void testMethod(int i) {
testMethod(i);
System.out.println(i);
}
Тут System.out.println (i); буде неодноразово натискатися на стек, коли буде викликаний testMethod.
Одним з (необов’язкових) аргументів JVM є розмір стека. Це -Xss. Я не знаю, яке значення за замовчуванням, але якщо загальна кількість матеріалів у стеку перевищує це значення, ви отримаєте цю помилку.
Як правило, причиною цього є нескінченна рекурсія, але якби ви це бачили, ваша траса стека мала б більше 5 кадрів.
Спробуйте додати аргумент -Xss (або збільшити значення одного), щоб побачити, чи це не зникне.
Що насправді викликає java.lang.StackOverflowError, як правило, це ненавмисна рекурсія. Для мене це часто, коли я мав намір назвати супер метод для перевизначеного методу. Такі як у цьому випадку:
public class Vehicle {
public void accelerate(float acceleration, float maxVelocity) {
// set the acceleration
}
}
public class SpaceShip extends Vehicle {
@Override
public void accelerate(float acceleration, float maxVelocity) {
// update the flux capacitor and call super.accelerate
// oops meant to call super.accelerate(acceleration, maxVelocity);
// but accidentally wrote this instead. A StackOverflow is in our future.
this.accelerate(acceleration, maxVelocity);
}
}
По-перше, корисно знати, що відбувається за кадром, коли ми викликаємо функцію. Аргументи та адреса, де був викликаний метод, штовхаються у стек (див. Http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management ), щоб викликаний метод міг отримати доступ до аргументів і щоб, коли викликаний метод завершено, виконання може тривати і після виклику. Але оскільки ми називаємо this.accelerate (прискорення, maxVelocity) рекурсивно (рекурсія є вільною, коли метод викликає себе. Для отримання додаткової інформації див. Http://en.wikipedia.org/wiki/Recursion_(computer_science)) ми потрапили в ситуацію, відому як нескінченна рекурсія, і ми продовжуємо накопичувати аргументи та адресу повернення в стек викликів. Оскільки стек викликів має обмежений розмір, з часом у нас закінчується простір. Закінчення місця у стеці викликів називається переповненням. Це тому, що ми намагаємося використовувати більше місця в стеку, ніж у нас, і дані буквально переповнюють стек. У мові програмування Java це призводить до винятку виконання java.lang.StackOverflow і негайно зупиняє програму.
Наведений вище приклад дещо спрощений (хоча це трапляється зі мною більше, ніж я хотів би визнати.) Те саме може статися більш круглим способом, що ускладнює відстеження. Однак загалом StackOverflow зазвичай досить легко вирішити, як тільки він відбувається.
Теоретично також можливе переповнення стека без рекурсії, але на практиці це здається досить рідкісною подією.
java.lang.StackOverflowError
Помилка java.lang.StackOverflowError
, яка вказує на те, що стек програми вичерпаний через глибоку рекурсію, тобто ваша програма / сценарій повторюється занадто глибоко.
Клас StackOverflowError
extends, VirtualMachineError
який вказує на те, що у JVM закінчилися або закінчилися ресурси, і він не може працювати далі. Те, VirtualMachineError
що розширює Error
клас, використовується для позначення тих серйозних проблем, які програма не повинна виявити. Метод може не оголошувати такі помилки у своємуthrow
реченні, оскільки ці помилки є ненормальними умовами, яких ніколи не передбачалося.
Minimal, Complete, and Verifiable Example
:
package demo;
public class StackOverflowErrorExample {
public static void main(String[] args)
{
StackOverflowErrorExample.recursivePrint(1);
}
public static void recursivePrint(int num) {
System.out.println("Number: " + num);
if(num == 0)
return;
else
recursivePrint(++num);
}
}
Number: 1
Number: 2
.
.
.
Number: 8645
Number: 8646
Number: 8647Exception in thread "main" java.lang.StackOverflowError
at java.io.FileOutputStream.write(Unknown Source)
at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
at java.io.BufferedOutputStream.flush(Unknown Source)
at java.io.PrintStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source)
at java.io.OutputStreamWriter.flushBuffer(Unknown Source)
at java.io.PrintStream.newLine(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:11)
at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
.
.
.
at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
Коли виклик функції викликається програмою Java, кадр стека виділяється в стек викликів . stack frame
Містить параметри, що викликається метод його локальних параметрів, а також зворотну адресу методу. Адреса повернення позначає точку виконання, з якої виконання програми триватиме після повернення викликаного методу. Якщо місця для нового фрейму стека не StackOverflowError
залишається, їх викидає віртуальна машина Java (JVM).
Найпоширенішим випадком, який може вичерпати стек програми Java, є рекурсія. У рекурсії метод викликає себе під час його виконання. Recursion
один з найпотужніших методів програмування загального призначення, але його слід застосовувати з обережністю, StackOverflowError
щоб уникнути цього.
Коли виклик функції викликається програмою Java, у стеку викликів виділяється кадр стека. Фрейм стека містить параметри викликаного методу, його локальні параметри та адресу повернення методу.
Адреса повернення позначає точку виконання, з якої виконання програми триватиме після повернення викликаного методу. Якщо місця для нового фрейму стека не залишається, StackOverflowError видається віртуальною машиною Java (JVM) .
Найпоширенішим випадком, який може вичерпати стек програми Java, є рекурсія.
Будь ласка, подивіться
Я створив програму зі сплячим режимом, в якій створив два класи POJO, обидва з об’єктом один одного як члени даних. Коли в основному методі я намагався зберегти їх у базі даних, я також отримав цю помилку.
Це відбувається тому, що обидва класи посилаються один на одного, отже, створюється цикл, який спричиняє цю помилку.
Отже, перевірте, чи існують такі стосунки у вашій програмі.
Рішення для користувачів Hibernate при аналізі даних:
У мене виникла ця помилка, оскільки я аналізував список об'єктів, зіставлених з обох сторін @OneToMany
та @ManyToOne
в json, використовуючи jackson, що спричинило нескінченний цикл.
Якщо ви потрапили в ту ж ситуацію, ви можете вирішити цю проблему, використовуючи @JsonManagedReference
та @JsonBackReference
анотації.
Визначення з API:
JsonManagedReference ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html ):
Анотація, що використовується для позначення того, що анотоване властивість є частиною двостороннього зв'язку між полями; і що його роль є "батьківським" (або "прямим") посиланням. Тип значення (клас) властивості повинен мати єдину сумісну властивість, анотовану JsonBackReference. Зв’язок обробляється таким чином, що властивість, котра коментується цією анотацією, обробляється нормально (серіалізується нормально, спеціальної обробки для десеріалізації немає); саме відповідне зворотне посилання вимагає особливої обробки
JsonBackReference: ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html ):
Анотація, що використовується для позначення того, що пов'язане властивість є частиною двостороннього зв'язку між полями; і що його роль полягає в "дочірньому" (або "зворотному") посиланні. Тип значення властивості повинен бути компонентом: він не може бути колекцією, картою, масивом або переліком. Зв'язок обробляється таким чином, що властивість, коментована цією анотацією, не є серіалізованою; і під час десеріалізації його значення встановлюється як екземпляр, який має посилання "керований" (прямий).
Приклад:
Owner.java:
@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;
Car.java:
@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;
Іншим рішенням є використання, @JsonIgnore
яке просто встановить значення null для поля.
Винятки із переповнення стека можуть виникати, коли стек ниток продовжує збільшуватися в розмірі до досягнення максимального обмеження.
Налаштування параметрів розмірів стеку (Xss та Xmso) ...
Я пропоную вам переглянути це посилання: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 Існує багато можливих причин StackOverflowError, як ви можете бачити за посиланням ....
У моєму випадку я маю дві діяльності. Під час другого заходу я забув поставити супер на метод onCreate.
super.onCreate(savedInstanceState);
StackOverflowError
, я не думаю, що він відповідає на питання. Я думаю, що правильна відповідь повинна перерахувати інші способи отримати цей виняток, ніж використання занадто великої кількості рекурсії, або сказати, що точно не існує іншого способу отримати такий виняток, крім кидання його вручну.