"pool-2-thread-1" prio=10 tid=0x007b0d88 nid=0x25 runnable [0xb0bff000..0xb0c01688]
at java.util.HashMap.put(HashMap.java:420)
at org.apache.velocity.util.introspection.ClassMap$MethodCache.get(ClassMap.java:271)
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)
? 前陣子, 在查閱 JDK bug 庫的時候, 曾經(jīng)看到過關(guān)于非正確使用Hashmap導(dǎo)致jvm的掛起 的問題。具體有如下解釋: This is a classic symptom of an incorrectly synchronized use of HashMap. Clearly, the submitters need to use a thread-safe HashMap. If they upgraded to Java 5, they could just use ConcurrentHashMap. If they can't do this yet, they can use either the pre-JSR166 version, or better, the unofficial backport as mentioned by Martin. If they can't do any of these, they can use Hashtable or synchhronizedMap wrappers, and live with poorer performance. In any case, it's not a JDK or JVM bug. 我們知道Hashmap不是讀寫線程安全的, 如果僅僅是全部只讀才是線程安全的。 這是JDK文檔的解釋: Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the? Collections.synchronizedMap?method. 雖然, 我們知道Hashmap在被并發(fā)讀寫使用的時候, 會跑出ConcurrentModificationException這個異常, 但是JDK文檔明確指出, 這個異常拋出是屬于 fail-fast 的一個設(shè)計方法, 目的是為了開發(fā)者能及早的意識到線程安全問題發(fā)生。 但是, 這個fail-fast不是一定會發(fā)生, 而是可能會發(fā)生的行為。 因此, 在一個不確定狀態(tài)下的下,jvm線程發(fā)生持續(xù)100%cpu行為是比較容易理解了(for (Entry<K,V> e = table[i]; e != null; e = e.next), 估計是這個代碼進(jìn)了死循環(huán)的狀態(tài))。 我對velocity代碼比較熟悉,結(jié)合了棧的情況, 可以看到velocity錯誤的使用了Hashmap作為方法cache的數(shù)據(jù)結(jié)構(gòu), 在并發(fā)處理初始化模板的時候,把機器的CPU 100%吃完的機會是會發(fā)生的。我感覺這個問題還是比較容易發(fā)生, 在短短的1個星期, 就觀察到2次這個現(xiàn)象。 我查閱了下velocity的Bug列表, 果然有人報告過這個問題: https://issues.apache.org/jira/browse/VELOCITY-718? 雖然bug已經(jīng)修復(fù), 但是要修改使用velocity的版本令我很擔(dān)憂。 velocity在某些細(xì)節(jié)處理的兼容性上非常糟糕。 曾經(jīng)升級過一次, 出現(xiàn)過無數(shù)的不兼容的行為。 這個bug的大部分因該出現(xiàn)系統(tǒng)的初始化階段, 要么系統(tǒng)起來, 要么系統(tǒng)啟動失敗。 基于修改的風(fēng)險性, 很是糾結(jié)。