#java #stack-overflow
#java #переполнение стека
Вопрос:
У меня есть приложение Java, которое работает как служба. После нескольких дней работы я получаю ошибку переполнения стека. Проблема в том, что фрейм стека в моем источнике, в котором возникает ошибка, отсутствует в ошибке, о которой сообщает функция printStackTrace. Я понятия не имею, как найти, в чем проблема.
Это результат printStackTrace:
java.lang.StackOverflowError
at java.util.LinkedList.listIterator(LinkedList.java:684)
at java.util.SubList$1.<init>(AbstractList.java:700)
at java.util.SubList.listIterator(AbstractList.java:699)
at java.util.SubList$1.<init>(AbstractList.java:700)
at java.util.SubList.listIterator(AbstractList.java:699)
at java.util.SubList$1.<init>(AbstractList.java:700)
at java.util.SubList.listIterator(AbstractList.java:699)
... (this is repeated hundreds of times)
at java.util.SubList.listIterator(AbstractList.java:699)
at java.util.SubList$1.<init>(AbstractList.java:700)
at java.util.SubList.listIterator(AbstractList.java:699)
at java.util.SubList$1.<init>(AbstractList.java:700)
Комментарии:
1. Происходит ли это последовательно? Вы можете подключиться к нему с помощью профилировщика и найти горячие точки кода. Или, что еще лучше, настройте агент для выполнения дампа потока при ошибке. Я знаю, что это можно сделать автоматически при ошибках OutOfMemory, не уверен в других типах. Но это стоит изучить.
2. Некоторые рекурсивные алгоритмы, такие как рекурсивное обнаружение больших двоичных объектов, выигрывают от увеличения размера стека.
Ответ №1:
Ваша проблема может быть воспроизведена следующим образом:
List<String> l = ...;
for (int i = 0; i < 2800; i ) {
l = l.subList(...);
}
l.listIterator(...);
Итак, обратите внимание на все вызовы subList()
и убедитесь, что они не образуют длинную цепочку подсписков, как указано выше.
Такая цепочка представляет собой рекурсивную структуру (каждый подсписок хранит ссылку на свой родительский список), и вызов listIterator()
очень длинной цепочки вызывает очень глубокую рекурсию, которая вызывает переполнение стека.
Комментарии:
1. Да, именно так это и выглядело для меня. Если возможно, попробуйте получить подсписок из исходного списка, если вам это нужно.
2.Также: интересно, что
SubList.subList()
не создается подсписок на основе оригиналаList
. На мой взгляд, это была бы хорошая оптимизация.3. Да, возможно, это причина. Я использовал подсписок для поворота журнала производительности: сначала я удалил записи с помощью подсписка, а затем добавил новые записи в конец. Это повторялось тысячи раз, поэтому … однако вопрос остается без ответа: как найти точку в моем источнике, если она глубоко спрятана в стеке, и printStackTrace не может ее достичь … может быть, мне стоит попробовать комментарий @G_H.
Ответ №2:
Ищите варианты использования List.subList()
метода — это единственное место, где вы можете получить экземпляр java.util.SubList
(класс с видимостью пакета).
Вы также можете попробовать перейти на более новую версию Java — ваша трассировка стека не соответствует структуре классов списка в текущих версиях, поэтому ваша проблема может не возникнуть (или привести к другой ошибке, которую легче диагностировать) в более текущей версии.