Java ClassLoader问题或并发错误



在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。在NoClassDefFoundError0块内部移动prp初始值设定项并没有什么区别。此外,"旧"CommonStuff和"新"prp中的"老"prp没有以任何方式连接(因为"旧"one_answers"新的"CommonStuff是JVM的不同类)。这一切使得同时修改prp的可能性看起来相当奇怪。

我相信原因在另一个地方。请注意堆栈跟踪的第一行:异常是由HashtableEnumerator引发的。以下是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()是最简单的方法。

最新更新