"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 庫(kù)的時(shí)候, 曾經(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ā)讀寫使用的時(shí)候, 會(huì)跑出ConcurrentModificationException這個(gè)異常, 但是JDK文檔明確指出, 這個(gè)異常拋出是屬于 fail-fast 的一個(gè)設(shè)計(jì)方法, 目的是為了開發(fā)者能及早的意識(shí)到線程安全問題發(fā)生。 但是, 這個(gè)fail-fast不是一定會(huì)發(fā)生, 而是可能會(huì)發(fā)生的行為。 因此, 在一個(gè)不確定狀態(tài)下的下,jvm線程發(fā)生持續(xù)100%cpu行為是比較容易理解了(for (Entry<K,V> e = table[i]; e != null; e = e.next), 估計(jì)是這個(gè)代碼進(jìn)了死循環(huán)的狀態(tài))。 我對(duì)velocity代碼比較熟悉,結(jié)合了棧的情況, 可以看到velocity錯(cuò)誤的使用了Hashmap作為方法cache的數(shù)據(jù)結(jié)構(gòu), 在并發(fā)處理初始化模板的時(shí)候,把機(jī)器的CPU 100%吃完的機(jī)會(huì)是會(huì)發(fā)生的。我感覺這個(gè)問題還是比較容易發(fā)生, 在短短的1個(gè)星期, 就觀察到2次這個(gè)現(xiàn)象。 我查閱了下velocity的Bug列表, 果然有人報(bào)告過這個(gè)問題: https://issues.apache.org/jira/browse/VELOCITY-718? 雖然bug已經(jīng)修復(fù), 但是要修改使用velocity的版本令我很擔(dān)憂。 velocity在某些細(xì)節(jié)處理的兼容性上非常糟糕。 曾經(jīng)升級(jí)過一次, 出現(xiàn)過無(wú)數(shù)的不兼容的行為。 這個(gè)bug的大部分因該出現(xiàn)系統(tǒng)的初始化階段, 要么系統(tǒng)起來(lái), 要么系統(tǒng)啟動(dòng)失敗。 基于修改的風(fēng)險(xiǎn)性, 很是糾結(jié)。