在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
之后应用程序与CCD_ 1一起完全失效,直到应用程序服务器被重新启动。
完整堆栈跟踪显示问题的根源是静态初始值设定项中的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
。在NoClassDefFoundError
0块内部移动prp
初始值设定项并没有什么区别。此外,"旧"CommonStuff
和"新"prp
中的"老"prp
没有以任何方式连接(因为"旧"one_answers"新的"CommonStuff
是JVM的不同类)。这一切使得同时修改prp
的可能性看起来相当奇怪。
我相信原因在另一个地方。请注意堆栈跟踪的第一行:异常是由Hashtable
的Enumerator
引发的。以下是putAll
方法的代码(与JDK8一样,可能多年没有更改):
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
这里是抛出异常的Enumerator
,它不是prp
,而是参数的Enumerator
。
因此,异常与prp
无关,而是与System.getProperties()
返回的Map
有关。原因是迭代系统属性映射不是线程安全的。似乎另一个线程正在同时修改它。
您需要以不同的方式初始化prp
。我认为clone()
是最简单的方法。