【问题标题】:java.util.Sublist throwing StackOverFlowErrorjava.util.Sublist 抛出 StackOverFlowError
【发布时间】:2010-09-24 19:09:23
【问题描述】:

我们在生产中偶尔会遇到与执行 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)
 ...

【问题讨论】:

  • 您使用的是哪个版本的 JDK?来自开放 JDK 的子列表似乎没有这个无限循环问题:grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…
  • 原因如下(可变列表的子列表),但看起来您想要做的是从 crumb 列表中删除尾随元素,而不是用它们创建旧列表的新视图隐藏(子列表的作用)。

标签: java error-handling stack-overflow


【解决方案1】:

subList() 方法返回一个视图由原始列表支持

根据javadoc:

返回的列表的语义 如果 后备名单(即这个名单)是 以其他任何方式进行结构修改 而不是通过返回的列表。 (结构修改是那些 更改此列表的大小,或 否则以这种方式扰乱它 正在进行的迭代可能会产生 结果不正确。)

您正在对列表进行结构性更改,所以所有的赌注都没有了 - 任何事情都可能发生,包括无限递归,这似乎正在发生。

【讨论】:

  • 换句话说,你应该这样做:breadCrumbs = new ArrayList(breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1));
【解决方案2】:

我在使用 LinkedList 标准库和 fastutil objectarraylist 时遇到了完全相同的问题(fastutil 是 Java 集合框架的快速高效的内存实现)。

使用

window = window.subList(index+1, window.size());

导致堆栈溢出错误。我替换为

window = new LinkedList<>( window.subList(index+1, window.size()) );

一切正常。

希望对你有帮助

【讨论】:

    【解决方案3】:

    以下是相关来源的摘录:

    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() 说什么?

    【讨论】:

    • @Colin:你错过了$1 部分?
    【解决方案4】:

    问题是由于 breadCrumbs 是 LinkedList 引起的——我们向 LinkedList 添加了太多项目,调用 subList 暴露了这个问题。

    【讨论】:

      【解决方案5】:

      我不认为是因为 LinkedList。在递归地对同一个列表调用 subList 时,我遇到了同样的错误。我认为每次调用方法 subList 时,它的开始/结束索引都会被推入堆栈。如果该列表很大,因此调用该方法的次数过多,则会发生 StackOverFlowError。

      【讨论】:

        【解决方案6】:

        问题在于 AbstractList.java(ArrayList 的基类)实现 subList 方法的方式。它通过父指针、偏移量和大小创建子列表(也称为视图)。 如果您在这样的 subList 上调用 subList,您会得到指向该列表的父指针,该列表本身具有父指针(等)

        子列表上的某些操作(例如添加)以递归方式工作。如果父指针的层次结构非常深,则会出现 StackOverflowError。

        以下 sn-p 显示了孤立的问题:

        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

        【讨论】:

        • 我们没有递归使用 subList 或调用“subList on ... subList”
        猜你喜欢
        • 2015-10-28
        • 2012-04-29
        • 2013-01-12
        • 2018-07-10
        • 2023-03-10
        • 1970-01-01
        • 2017-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多