vevocity在做渲染时会调用 org.apache.velocity.util.introspection.ClassMap$MethodCache.get方法 该方法完整代码(velocity1.6.1版本)如下
private final Map cache = new HashMap();
public Method get(final String name, final Object [] params)
throws MethodMap.AmbiguousException
{
String methodKey = makeMethodKey(name, params);
Object cacheEntry = cache.get(methodKey);
if (cacheEntry == CACHE_MISS)
{
// We looked this up before and failed.
return null;
}
if (cacheEntry == null)
{
try
{
// That one is expensive...
cacheEntry = methodMap.find(name, params);
}
catch(MethodMap.AmbiguousException ae)
{
/*
* that's a miss :-)
*/
cache.put(methodKey, CACHE_MISS);
throw ae;
}
cache.put(methodKey,
(cacheEntry != null) ? cacheEntry : CACHE_MISS);
}
// Yes, this might just be null.
return (Method) cacheEntry;
}
这里需要注意的是cache部分的实现:cache = new HashMap();由于HashMap不是线程安全的,这样在并发较高的情况下会导致线程安全的问题.使用中发现,在高并发的情况下,服务器的CPU可能会暴涨,在10%左右,java进程的cpu在400%左右,而且一直下不去,即使没有访问.通过分析java线程,发现在HashMap.get方法会堵死,get里面的while循环会新数据的加入变得不可预料.jstack片段:
"TP-Processor28" daemon prio=10 tid=0x00002aab4db7e000 nid=0x2a29 runnable [0x0000000042e37000]
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.get(HashMap.java:303)
at org.apache.velocity.util.introspection.ClassMap$MethodCache.get(ClassMap.java:259)
at org.apache.velocity.util.introspection.ClassMap.findMethod(ClassMap.java:102)
at org.apache.velocity.util.introspection.IntrospectorBase.getMethod(IntrospectorBase.java:105)
at org.apache.velocity.util.introspection.Introspector.getMethod(Introspector.java:94)
at org.apache.velocity.runtime.parser.node.PropertyExecutor.discover(PropertyExecutor.java:118)
at org.apache.velocity.runtime.parser.node.PropertyExecutor.<init>(PropertyExecutor.java:56)
at org.apache.velocity.util.introspection.UberspectImpl.getPropertyGet(UberspectImpl.java:246)
at org.apache.velocity.runtime.parser.node.ASTIdentifier.execute(ASTIdentifier.java:148)
at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:252)
at org.apache.velocity.runtime.parser.node.ASTReference.render(ASTReference.java:332)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:336)
at org.apache.velocity.Template.merge(Template.java:328)
这种堵塞容易发生在多CPU高并发的机器上,HashMap的桶在存了大量的数据后get操作的for循环取对象的操作在同时有读有写的情况下变得不可预知。在使用循环时,一定注意退出条件,当有极端境况发生循环无法退出时就悲剧了。
velocity1.6.4版本对该部分的cache已经做了处理,使用了线程安全的cache:
cache = MapFactory.create(false);MapFactory对并发map进行了处理,具体代码可见 org.apache.velocity.util.MapFactory类
建议大家使用1.6.4以上版本的velocity,同时在使用hashMap的时候一定要注意线程安全的问题.