问题
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]
looking at the source code this is the impl of get() (Collections.java:1454):
public V get(Object key) {return m.get(key);}
so this should only be possible if somehow this.m = this, but i cannot reproduce such a scenario.
how is this even possible?
回答1:
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);
}
}
(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:
- 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.
Like in this code:
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).
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.
来源:https://stackoverflow.com/questions/34384953/stackoverflowerror-from-collections-unmodifiablemap-get