2015-03-13 19 views
3

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

这怎么可能? Collections中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的bug ...

这个问题是否对别人很熟悉?

+3

你可以在这里添加一些代码吗? – 2015-03-13 06:56:22

+0

看到你的代码会有帮助。 – Yellen 2015-03-13 06:56:51

+0

你最近如何调用它? – Makoto 2015-03-13 07:38:44

回答

2

下面是从Java 7u40的UnmodifiableCollection类的代码(你应该在JDK的Java 7u51实际的源代码)

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(); 
       } 
      }; 
     } 

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

我能想到的一个合理的解释:

  • 如果您的应用程序是(出于某种原因)包装在不可修改的集合不可修改的集合,以N个级别,然后创建一个迭代器将导致N * 2层堆栈帧。对于足够大的N,这会导致堆栈溢出。

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

+0

感谢您的反馈。问题是这里的堆栈深度是41000,所以我们不会经常缠绕在包装上。虽然,包装发生。 – Cowboy 2015-03-13 09:39:16