java - 来自 Collections.unmodifiableMap get() 的 StackOverflowError?

标签 java collections

我刚刚收到以下堆栈跟踪:

2015-12-20 07:43:36.151 -0800 ERROR o.s.s.s.TaskUtils$LoggingErrorHandler [taskExecutor-6] Unexpected error occurred in scheduled task.
java.lang.StackOverflowError
    at java.util.Collections$UnmodifiableMap.get(Collections.java:1454) ~[?:1.8.0_65]
    at java.util.Collections$UnmodifiableMap.get(Collections.java:1454) ~[?:1.8.0_65]
    at java.util.Collections$UnmodifiableMap.get(Collections.java:1454) ~[?:1.8.0_65]
    at java.util.Collections$UnmodifiableMap.get(Collections.java:1454) ~[?:1.8.0_65]

查看源代码,这是 get() (Collections.java:1454) 的实现:

public V get(Object key)                 {return m.get(key);}

所以这只有在 this.m = this 的情况下才有可能,但我无法重现这种情况。

这怎么可能?

最佳答案

详细说明 Sotirios 的评论:行为可以用这样的东西重现:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class UnmodifiableMapStackOverflow
{
    public static void main(String[] args)
    {
        int depth = 20000; 
        test(depth);
    }

    private static void test(int depth)
    {
        Map<String, String> map = new HashMap<String, String>();
        map.put("X", "Y");
        for (int i =0; i<depth; i++)
        {
            map = Collections.unmodifiableMap(map);
        }
        String value = map.get("X");
        System.out.println("At "+depth+" got "+value);
    }
}

(depth 所需的值可能取决于很多很多因素 - 有疑问的是,您可能必须增加它才能观察效果)。

当然,这段代码显然是错误的。关键是你可能会不小心做类似的事情。更复杂的情况可能如下:

  • map 存储在一个字段中,使用setMap方法
  • map 在 getMap 方法中返回。但是由于您通常不应返回可修改的内部数据结构,因此会返回一个不可修改的 View
  • 再次设置这个不可修改的 View ,在每次调用期间在原始 mal 周围形成一个“层”。

就像这段代码:

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public class UnmodifiableMapStackOverflowComplex
{
    public static void main(String[] args)
    {
        UnmodifiableMapStackOverflowComplex c = 
            new UnmodifiableMapStackOverflowComplex();

        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("X", "Y");
        c.setMap(map);

        for (int i=0; i<100000; i++)
        {
            Map<String, String> m = c.getMap();
            System.out.println("At "+i+": "+m.get("X"));
            c.setMap(m);
        }
    }

    private Map<String, String> map;
    Map<String, String> getMap()
    {
        // It's a good practice to only return unmodifiable VIEWS
        // on internal data structures:
        return Collections.unmodifiableMap(map);        
    }
    void setMap(Map<String, String> map)
    {
        this.map = map;
    }


}

到现在为止,这只是一个猜测,但这是我能想到的唯一可能的原因(除非你在某处进行了一些讨厌的反射黑客攻击)。

为了检测这里是否确实是这种情况,您可以尝试在最终调用 Map#get 的方法处设置断点,并在调试器中检查对象。

关于java - 来自 Collections.unmodifiableMap get() 的 StackOverflowError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34384953/

相关文章:

java - 如何迭代列表以仅获取特定属性

.net - 有了这个 .NET 事件,传入这个 IList 实例可以吗?

scala:以功能方式从集合中累积一个var(即没有vars)

java - 从 HashMap 中检索记录

两个 Shape 对象之间的 Java 碰撞检测?

java - 返回非隐藏文件

java - "Location is required"加载FXML文件时出现异常

java - 如何利用投入的设备成本找到一笔资金的最有效利用?

java - 是否可以阻止 Spring Boot 应用程序尝试连接到 IBM MQ?

java - 动态数组列表用于迭代对象Arraylist并根据对象名称获取结果