来自Collections.unmodifiableMap get()的StackOverflowError吗? [英] StackOverflowError from Collections.unmodifiableMap get()?
问题描述
我刚刚获得了以下堆栈跟踪:
i was just handed the following stack trace:
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):
looking at the source code this is the impl of get() (Collections.java:1454):
public V get(Object key) {return m.get(key);}
所以只有在不知不觉中this.m = this的情况下才有可能,但我无法重现这种情况.
so this should only be possible if somehow this.m = this, but i cannot reproduce such a scenario.
这怎么可能?
推荐答案
要详细说明Sotirios的评论:可以通过以下方式重新表现该行为:
To elaborate the comment by Sotirios: The behavior can be reporoduced with something like this:
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
所需的值可能取决于很多因素,毫无疑问,您可能必须增加该值才能观察到效果).
(The value that is required for the depth
may depend on many, many factors - in doubt, you may have to increase it to observe the effect).
当然,此代码是公然的,显然是错误的.关键是您可能会意外地执行类似的操作.更为复杂的情况如下:
Of course, this code is blatantly and obviously wrong. The key point is that you might accidentally do something similar. A more complex scenario might be the following:
- 使用
setMap
方法 将地图存储在字段中 - 通过
getMap
方法返回地图.但是由于您通常不应该返回可修改的内部数据结构,因此会返回不可修改的视图. - 再次设置此不可修改的视图,在每次通话期间在原始恶意事件周围造成一个层".
- The map is stored in a field, using a
setMap
method - The map is returned in a
getMap
method. But becuase you should usually not return modifiable internal data structures, an unmodifiable view is returned. - This unmodifiable view is set again, causing one "layer" around the original mal during each call.
类似于此代码:
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;
}
}
直到现在,这只是一个猜测,但这是我能想到的唯一可能的原因(除非您在某处进行了一些讨厌的反射黑客).
Until now, this is just a guess, but the only possible reason that I can think of (unless you're doing some nasty reflection hacks somewhere).
为了检测在这里是否确实存在这种情况,您可以尝试在最终调用 Map#get
的方法上设置一个断点,并在调试器中检查对象.
In order to detect whether this is actually the case here, you might try to set a breakpoint at the method that eventually calls Map#get
, and inspect the object in the debugger.
这篇关于来自Collections.unmodifiableMap get()的StackOverflowError吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!