首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Hibernate3.1.X 多线程上BUG

2012-10-06 
Hibernate3.1.X 多线程下BUG刚写过一篇Java笔记-使用JConsole进行JVM性能监测,今天就又遇上99%,乐不开支拍

Hibernate3.1.X 多线程下BUG

刚写过一篇Java笔记-使用JConsole进行JVM性能监测,今天就又遇上99%,乐不开支拍拍手打开JConsole就要收拾它。

?

在Thread选项卡中看到许多HTTP的请求线程都阻塞在org.hibernate.util.SoftLimitMRUCache.get(SoftLimitMRUCache.java:51)?? 很快就发现下面这个Thread

?

Name: TP-Processor24
State: RUNNABLE
Total blocked: 87? Total waited: 21

?

Stack trace:
org.apache.commons.collections.ReferenceMap.getEntry(Unknown Source)
org.apache.commons.collections.ReferenceMap.get(Unknown Source)
org.hibernate.util.SoftLimitMRUCache.get(SoftLimitMRUCache.java:51)
org.hibernate.engine.query.QueryPlanCache.getNativeSQLQueryPlan(QueryPlanCache.java:107)
org.hibernate.impl.AbstractSessionImpl.getNativeSQLQueryPlan(AbstractSessionImpl.java:140)
org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:147)
org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:164)
com.mogoko.struts.logic.user.LeaveMesManager.getCommentByShopId(LeaveMesManager.java:302)
com.mogoko.struts.action.shop.ShopIndexBaseInfoAction.execute(ShopIndexBaseInfoAction.java:175)

?

?

?

LeaveMesManager.java:302是下面

?

list = (ArrayList) session.createSQLQuery(queryString).addEntity(“”,Leavemes.class).list();

?

?

?

显然是TP-Processor24进入SoftLimitMRUCache.get—>ReferenceMap.get–>ReferenceMap.getEntry没有返回,一共87个Thread被阻塞(21代表什么呢?)。

?

我们的环境如下:

?

Hibernate 3.1.2

?

Collections:2.1.1

?

分别打开了SoftLimitMRUCache.java和ReferenceMap.java。后者extends自AbstractMap,本身不提供线程安全保证,那就是SoftLimitMRUCache的问题了。看下面它的代码

?

?

?

?

?


public synchronized Object get(Object key) {
??Object result = softReferenceCache.get( key );//?? 第51行
??if ( result != null ) {
???strongReferenceCache.put( key, result );
??}
??return result;
?}

?

?public Object put(Object key, Object value) {
??softReferenceCache.put( key, value );
??return strongReferenceCache.put( key, value );
?}

?

?public int size() {
??return strongReferenceCache.size();
?}

?

?public int softSize() {
??return softReferenceCache.size();
?}

?

?public Iterator entries() {
??return strongReferenceCache.entrySet().iterator();
?}

?

?public Iterator softEntries() {
??return softReferenceCache.entrySet().iterator();
?}

?

?

?

除get函数外都没有synchronized,顶你的肺?? Hibernate3.1.X 多线程上BUG?? 看样子是每一次Hibernate’s Query在调用JDBC查询前都会去这个SoftLimitMRUCache先尝试从内存中查,从而减少数据库负载,之所以1次/周,是源于访问量不大,没有做好压力测试啊?? Hibernate3.1.X 多线程上BUG

?

再看看ReferenceMap.java

?


??? public Object get(Object key) {
??????? purge();
??????? Entry entry = getEntry(key);???????//?? 调用getEntry
??????? if (entry == null) return null;
??????? return entry.getValue();
??? }


??? private Entry getEntry(Object key) {
??????? if (key == null) return null;
??????? int hash = key.hashCode();
??????? int index = indexFor(hash);
??????? for (Entry entry = table[index]; entry != null; entry = entry.next) {
??????????? if ((entry.hash == hash) && key.equals(entry.getKey())) {
??????????????? return entry;
??????????? }
??????? }
??????? return null;
??? }


??? public Object put(Object key, Object value) {
??????? if (key == null) throw new NullPointerException(“null keys not allowed”);
??????? if (value == null) throw new NullPointerException(“null values not allowed”);


??????? purge();
??????? if (size + 1 > threshold) resize();


??????? int hash = key.hashCode();
??????? int index = indexFor(hash);
??????? Entry entry = table[index];
??????? while (entry != null) {
??????????? if ((hash == entry.hash) && key.equals(entry.getKey())) {
??????????????? Object result = entry.getValue();
??????????????? entry.setValue(value);
??????????????? return result;
??????????? }
??????????? entry = entry.next;
??????? }
??????? this.size++;
??????? modCount++;
??????? key = toReference(keyType, key, hash);
??????? value = toReference(valueType, value, hash);
??????? table[index] = new Entry(key, hash, value, table[index]);
??????? return null;
??? }


??? private void resize() {
??????? Entry[] old = table;
??????? table = new Entry[old.length * 2];


??????? for (int i = 0; i < old.length; i++) {
??????????? Entry next = old[i];
??????????? while (next != null) {
??????????????? Entry entry = next;
??????????????? next = next.next;
??????????????? int index = indexFor(entry.hash);
??????????????? entry.next = table[index];
??????????????? table[index] = entry;
??????????? }
??????????? old[i] = null;
??????? }
??????? threshold = (int)(table.length * loadFactor);
??? }


注意上面的四行粗蓝色代码,最多有三处会循环遍历/修改链表,多Thread环境下导致链表出现环路,结果infinite loop!


在Hibernate官网找到SoftLimitMRUCache的bug,有两条


1??? Concurrent access issues with both SoftLimitMRUCache and SimpleMRUCache


影响版本3.2.0.alpha1, 3.1.3?


再看看这个Infinite Loop Possible Through Non-synchronisd use LRUMap


讲的虽然是LRUMap,但根本原因仍在于SoftLimitMRUCache除get函数外没有同步导致。


对非同步的map多线程下带来的问题感兴趣可以看这里


HashMap.get() can cause an infinite loop!


2??? Use of session.createSQLQuery causes memory leak


?内存泄漏同样由于线程非安全导致。


?


下载Hibernate3.2.1的源码如下


?


public synchronized Object put(Object key, Object value) {
??softReferenceCache.put( key, value );
??return strongReferenceCache.put( key, value );
?}


?public synchronized int size() {
??return strongReferenceCache.size();
?}


?public synchronized int softSize() {
??return softReferenceCache.size();
?}


?public synchronized void clear() {
??strongReferenceCache.clear();
??softReferenceCache.clear();
?}


?


新版本中get/put/size/softSize函数和新增的clear函数都加上了synchronized同步。


?


?


Hibernate的bug查询地址是http://opensource.atlassian.com/projects/hibernate/secure/IssueNavigator.jspa


?


?


赶紧扔掉你的Hibernate3.1.X,换到Hibernate3.2.1以上吧,如果还有死循环问题我会。。

源自:http://www.mogoko.com/p/article/2385

热点排行