我们在生产中偶尔会遇到与执行SubList操作相关的StackOverFlowError错误。有没有人见过这样的东西,知道是什么原因造成的?
这是被调用的代码,用于触发错误:
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);
}
}
结果:
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)
...
答案 0 :(得分:6)
subList()方法返回由原始列表支持的视图。
根据javadoc:
返回的列表的语义 如果这个方法变得不确定 支持列表(即此列表)是 以任何方式进行结构修改 而不是通过返回的列表。 (结构修改是那些 这会改变这个列表的大小,或者 否则就会以这种方式扰乱它 正在进行的迭代可能会产生 结果不正确。)
您正在对列表进行结构更改,因此所有投注都已关闭 - 任何都可能发生,包括无限递归,这似乎正在发生。
答案 1 :(得分:2)
使用LinkedList标准库和fastutil objectarraylist我遇到了完全相同的问题(fastutil是Java集合框架的快速有效的内存实现)。
使用
window = window.subList(index+1, window.size());
导致stackoverflow错误。我换成了
window = new LinkedList<>( window.subList(index+1, window.size()) );
一切正常。
希望它可以提供帮助
答案 2 :(得分:0)
以下是相关来源的摘录:
681 public ListIterator<E> listIterator(final int index) {
...
687 return new ListIterator<E>() {
688 private ListIterator<E> i = l.listIterator(index+offset);
此StackOverflowError
表示l
以某种方式引用当前子列表,因此在无限循环中调用自己的listIterator()
。
breadCrumbs
来自哪里?它的getClass()
说什么?
答案 3 :(得分:0)
问题是由breadCrumbs作为LinkedList引起的 - 我们向LinkedList添加了太多项,并且调用subList暴露了这个问题。
答案 4 :(得分:0)
我不认为这是因为LinkedList。我以递归方式对同一列表调用subList时遇到了同样的错误。我认为每次调用方法subList时,它的开始/结束索引都被推入堆栈。如果该列表很大,因此调用该方法的次数太多,就会发生StackOverFlowError。
答案 5 :(得分:0)
问题在于AbstractList.java(ArrayList的基类)实现subList方法的方式。它通过父指针,偏移量和大小创建子列表(也称为视图)。 如果在这样的子列表上调用subList,则会获得指向列表的父指针,该列表本身具有父指针(等等)
子列表上的某些操作(例如添加)会递归工作。如果你有一个非常深的父指针层次结构,你会得到一个StackOverflowError。
以下代码段显示了隔离的问题:
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");
}
结论:不要像这样递归地使用subList:
breadCrumbs = breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1);
而是通过从末尾删除元素来设置长度。
我的博客上更详细的分析:http://programmingtipsandtraps.blogspot.com/2013/05/javautillistsublist-stackoverflowerror.html