最近协助一些BEA客户做调优,他们使用了Spring,出现了各种各样的性能问题,这些问题其实都是不容易重现的,其中,我自己捕获了一些ThreadDump,并report了给Spring JIRA。这个Case的情况是:Spring会偶然出现CPU 100%的情况,WebLogic Server崩溃,我后来分析了线程Dump,觉得是一种Lock Contention的情形,幸好,Juergen Hoeller很快给我Fixed了这个Bug:
http://jira.springframework.org/browse/SPR-4664
使用Java编程的同学都建议Review一下,呵呵:
这是2.5.4以前的代码:
/**
* Cache of TransactionAttributes, keyed by DefaultCacheKey (Method + target Class).
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
final
Map attributeCache
=
new
HashMap();
/**
* Determine the transaction attribute for this method invocation.
* <p>Defaults to the class's transaction attribute if no method attribute is found.
*
@param
method the method for the current invocation (never <code>null</code>)
*
@param
targetClass the target class for this invocation (may be <code>null</code>)
*
@return
TransactionAttribute for this method, or <code>null</code> if the method
* is not transactional
*/
public
TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
//
First, see if we have a cached value.
Object cacheKey
=
getCacheKey(method, targetClass);
synchronized
(
this
.attributeCache) {
Object cached
=
this
.attributeCache.get(cacheKey);
if
(cached
!=
null
) {
//
Value will either be canonical value indicating there is no transaction attribute,
//
or an actual transaction attribute.
if
(cached
==
NULL_TRANSACTION_ATTRIBUTE) {
return
null
;
}
else
{
return
(TransactionAttribute) cached;
}
}
else
{
//
We need to work it out.
TransactionAttribute txAtt
=
computeTransactionAttribute(method, targetClass);
//
Put it in the cache.
if
(txAtt
==
null
) {
this
.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else
{
if
(logger.isDebugEnabled()) {
logger.debug(
"
Adding transactional method [
"
+
method.getName()
+
"
] with attribute [
"
+
txAtt
+
"
]
"
);
}
this
.attributeCache.put(cacheKey, txAtt);
}
return
txAtt;
}
}
}
这是2.5.4 Fixed后的代码:
/**
* Cache of TransactionAttributes, keyed by DefaultCacheKey (Method + target Class).
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
final
Map attributeCache
=
CollectionFactory.createConcurrentMapIfPossible(
16
);
/**
* Determine the transaction attribute for this method invocation.
* <p>Defaults to the class's transaction attribute if no method attribute is found.
*
@param
method the method for the current invocation (never <code>null</code>)
*
@param
targetClass the target class for this invocation (may be <code>null</code>)
*
@return
TransactionAttribute for this method, or <code>null</code> if the method
* is not transactional
*/
public
TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
//
First, see if we have a cached value.
Object cacheKey
=
getCacheKey(method, targetClass);
Object cached
=
this
.attributeCache.get(cacheKey);
if
(cached
!=
null
) {
//
Value will either be canonical value indicating there is no transaction attribute,
//
or an actual transaction attribute.
if
(cached
==
NULL_TRANSACTION_ATTRIBUTE) {
return
null
;
}
else
{
return
(TransactionAttribute) cached;
}
}
else
{
//
We need to work it out.
TransactionAttribute txAtt
=
computeTransactionAttribute(method, targetClass);
//
Put it in the cache.
if
(txAtt
==
null
) {
this
.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else
{
if
(logger.isDebugEnabled()) {
logger.debug(
"
Adding transactional method [
"
+
method.getName()
+
"
] with attribute [
"
+
txAtt
+
"
]
"
);
}
this
.attributeCache.put(cacheKey, txAtt);
}
return
txAtt;
}
}
但是2.5.4 snapshot是未经很好测试的版本,客户一般不太敢用。
我不知道其实有多少客户真正地把Spring投入到高并发性环境下使用,
如果有,他们应该会能碰到我所碰到的情形。