StackOverflowError Collections :: unmodifiableList

时间:2015-03-13 06:55:12

标签: java stack-overflow

我在下面的StackOverflowError中发现了一个奇怪的随机错误。这不是确定性的,所以我很难捕获这个问题......我有一个Arraylist,它被包装到Collections :: unmodifiableList(...),然后使用for进行迭代(Val val :myValues)

这怎么可能?集合中的UnmodifiableList实现中的字段都是最终的,因此不能存在任何循环依赖(例如A-> B-> A))。不,这里也不涉及反思......

java.lang.StackOverflowError
at java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1064)        
at java.util.Collections$UnmodifiableCollection.iterator(Collections.java:1063)        
at java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1064)
at java.util.Collections$UnmodifiableCollection.iterator(Collections.java:1063)
at java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1064)
at java.util.Collections$UnmodifiableCollection.iterator(Collections.java:1063)
at java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1064)
at java.util.Collections$UnmodifiableCollection.iterator(Collections.java:1063)
at java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1064)
at com.server.ServerFunction$ServerFunctionResult.evaluateValues(ServerFunctionn.java:562)
at com.server.ServerFunction$ServerFunctionResult.access$300(ServerFunctionn.java:348)
at com.server.ServerFunction.perform(ServerFunction.java:1171)

生成此代码的代码(由于我无法在此处发布实际代码,因此已重新编写代码...):

public final static class ServerFunctionResult
{
    private final List<String> myValues;
    private final boolean myIsProcessed;

    public ServerFunctionResult(List<String> values, boolean isProcessed)
    {
        // Reduce object retention of empty list objects
        myValues = values.isEmpty() ? Collections.EMPTY_LIST : Collections.unmodifiableList(values);
        myIsProcessed = isProcessed;
    }

    public ServerFunctionResult evaluateValues()
    {
        if (!myIsProcessed)
        {
            for (String s : myValues) // <-- HERE IT THROWS
            {
                // Process values
            }

            return new ServerFunctionResult(myValues, true);
        }

        return this;
    }
}

这是Java 7u51。我试图绕过这个问题,但现在我怀疑是JVM错误......

这个问题对其他人来说是否熟悉?

1 个答案:

答案 0 :(得分:2)

以下是来自Java 7u40的UnmodifiableCollection类的代码:

    static class UnmodifiableCollection<E> 
            implements Collection<E>, Serializable {
        private static final long serialVersionUID = 1820017752578914078L;
        final Collection<? extends E> c;
        UnmodifiableCollection(Collection<? extends E> c) {
            if (c==null)
                throw new NullPointerException();
            this.c = c;
        }

        ....

        public Iterator<E> iterator() {
            return new Iterator<E>() {
                private final Iterator<? extends E> i = c.iterator();
                public boolean  hasNext() {return i.hasNext();}
                public E  next() {return i.next();}
                public void  remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }

(您应该在JDK for Java 7u51中拥有实际的源代码。)

如您所见,当您在不可修改的集合上调用iterator时,它会在匿名类上创建一个实例。该类的构造函数调用c.iterator() ...其中c是包装类。但是,堆栈跟踪意味着c本身就是一个不可修改的集合。

我能想出一个似是而非的解释:

  • 如果您的应用程序(由于某种原因)将不可修改的集合中的不可修改的集合包装到N级别,那么创建迭代器将导致N * 2级别的堆栈帧。对于足够大的N,这将导致堆栈溢出。

还有其他可能的解释涉及使用反射(或字节码工程)来打破类型抽象边界,或者假设的JVM(可能是JIT编译器)错误。坦率地说,JVM错误解释是相当难以置信的,但如果我怀疑我会从升级到最新的Java 7版本开始。 (实际上,我可能会这样做!7u51在Java 7平台上缺少1年的安全补丁和错误修复。)