Reputation: 12876
We are getting occasional StackOverFlowError errors in production related to doing a SubList operation. Has anyone seen something like this before and know what could cause it?
This is the code that gets called that triggers the error:
FacesContext context = FacesContext.getCurrentInstance();
String newViewID = context.getViewRoot().getViewId();
if (newViewID != null) {
if (breadCrumbs.contains(newViewID)) {
// Trims the list upon going back to allow for multiple back button requests.
// This is lightweight and not intended for a complex circular navigation.
breadCrumbs = breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1);
} else {
breadCrumbs.add(newViewID);
}
}
The result :
Caused By: java.lang.StackOverflowError
at java.util.SubList$1.<init>(AbstractList.java:688)
at java.util.SubList.listIterator(AbstractList.java:687)
at java.util.SubList$1.<init>(AbstractList.java:688)
at java.util.SubList.listIterator(AbstractList.java:687)
...
Upvotes: 6
Views: 2645
Reputation: 1379
I had the exact same problem using both LinkedList standard library and fastutil objectarraylist (fastutil are a fast and efficient memory implementation of Java collection framework).
Using
window = window.subList(index+1, window.size());
caused stackoverflow error. I replaced with
window = new LinkedList<>( window.subList(index+1, window.size()) );
and everything worked fine.
Hopes it can help
Upvotes: 3
Reputation: 1885
The problem lies in the way AbstractList.java (base class of ArrayList) implements the subList method. It creates the sublist (aka view) by means of a parent pointer, an offset and a size. If you call subList on such a subList you get parent pointer pointing to the list which itself has a parent pointer (etc.)
Some operations (e.g. add) on sublists work recursivly. If you have a very deep hierarchy of parent pointers you get a StackOverflowError.
The following snippet shows the problem isolated:
public static void main(String[] args) {
List<String> lst = new ArrayList<String>();
lst.add("");
for (int i = 0; i < 50000; i++) {
lst.set(0, "test");
lst = lst.subList(0, 1);
}
lst.add("test2");
}
Conclusion: Do not use subList recursively like this:
breadCrumbs = breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1);
Instead set the length by removing elements from the end.
More detailled analysis on my blog: http://programmingtipsandtraps.blogspot.com/2013/05/javautillistsublist-stackoverflowerror.html
Upvotes: 0
Reputation: 6487
I don't think it is because of LinkedList. I have got the same error while calling subList against the same list recursively. I think that each time the method subList is called, its begin/end indices are pushed in stack. If that list is huge and therefore too many times that method is called, StackOverFlowError occurs.
Upvotes: 0
Reputation: 12876
The problem was caused by breadCrumbs being a LinkedList-- we were adding too many items to the LinkedList and calling subList exposed this problem.
Upvotes: 0
Reputation: 1108692
Here's an extract of the relevant source:
681 public ListIterator<E> listIterator(final int index) {
...
687 return new ListIterator<E>() {
688 private ListIterator<E> i = l.listIterator(index+offset);
This StackOverflowError
indicates that l
is somehow referring to the current sublist and is thus calling its own listIterator()
in an infinite loop.
Where does the breadCrumbs
come from? What does its getClass()
say?
Upvotes: 0
Reputation: 86764
The subList() method returns a view backed by the original list.
According to the javadoc:
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
You are making structural changes to the list, so all bets are off -- anything can happen, including infinite recursion, which is what appears to be happening.
Upvotes: 6