WebLogic应用正常运行了几周后,我突然收到异常:
<Oct 25, 2014 9:31:11 PM EDT> <Error> <HTTP> <BEA-101020>
<[ServletContext@60724164[app:whatever3000 module:whatever3000.war path:
spec-version:2.5]] Servlet failed with Exception
java.lang.ExceptionInInitializerError
之后,使用
NoClassDefFoundError
彻底关闭应用程序,直到重新启动应用程序服务器。完整的堆栈跟踪显示问题的根源是静态初始化程序中的
ConcurrentModificationException
。具体来说,等效/最小化的代码如下:
package a;
import b;
public class Whatever {
void doIt()
{
Password p = new Password();
}
}
package b;
public final class Password implements Serializable
{
private static final int PARAM1 = CommonStuff.someStaticMethod();
...
}
import java.util.Properties;
public class CommonStuff
{
private static Properties prp = new Properties();
static {
CommonStuff.load();
}
public static void load()
{
prp.putAll(System.getProperties()); <---FAIL
这是异常的来源:
java.util.ConcurrentModificationException
at java.util.Hashtable$Enumerator.next(Hashtable.java:1017)
at java.util.Hashtable.putAll(Hashtable.java:469)
at b.CommonStuff.load(CommonStuff.java:55)
at b.CommonStuff.<clinit>(CommonStuff.java:77)
at b.Password.<clinit>(Password.java:44)
at a.doIt(Whatever.java:99)
因此,似乎在应用程序运行时的某个时刻,WebLogic决定从
package b
重新加载类,但是当静态块运行时,它发现Properties
对象已被修改。我不知道它是同时被调用还是被多次调用。可能是
Properties
对象是当应用程序第一次重新加载并且CommonStuff
类的重新加载尝试再次调用putAll()
时创建的原始实例。如果这样做会有所帮助:
private static Properties prp = null;
static {
CommonStuff.prp = new Properties();
CommonStuff.load();
}
我不能盲目尝试,因为它在一家大公司的生产应用程序中。因此,我试图了解错误原因以及在半夜重新加载类时如何对这些变量进行属性初始化。
有任何想法吗?
这可能是WebLogic ClassLoader问题吗?
最佳答案
在初始化该类之前,类/实例无法访问某些类的成员。因此,在静态构造函数返回之前,没有人可以访问新创建的prp
。将prp
初始值设定项移到static {}
块上没有什么区别。同样,“旧” prp
和“新” CommonStuff
中的“旧” prp
也不以任何方式连接(因为“旧”和“新” CommonStuff
是JVM的不同类)。所有这些使得同时进行prp
修改的可能性看起来很奇怪。
我相信原因在另一个地方。注意堆栈跟踪的第一行:Enumerator
的Hashtable
引发异常。这是putAll
方法的代码(与JDK 8一样,可能多年没有改变):
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
这是引发异常的
Enumerator
-不是prp
,而是参数的Enumerator
。因此,该异常与
prp
无关,而与Map
返回的System.getProperties()
有关。原因是,遍历系统属性映射不是线程安全的。似乎另一个线程正在同时修改它。您需要以不同的方式初始化
prp
。我认为clone()
是最简单的方法。