【问题标题】:Java ClassLoader Issue or Concurrency Error?Java ClassLoader 问题或并发错误?
【发布时间】:2014-10-28 14:28:42
【问题描述】:

在一个 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 类加载器问题吗?

【问题讨论】:

    标签: java weblogic classloader static-initializer


    【解决方案1】:

    类/实例在初始化此类之前无法访问某些类的成员。因此,在静态构造函数返回之前,没有人可以访问新创建的prp。在 static {} 块内移动 prp 初始化程序没有区别。此外,“旧”CommonStuff 和“新”prp 中的“旧”prp 也没有以任何方式连接(因为“旧”和“新”CommonStuff 是 JVM 的不同类)。这一切都使得同时修改prp 的可能性看起来很奇怪。

    我相信原因在另一个地方。注意堆栈跟踪的第一行:EnumeratorHashtable 抛出异常。下面是putAll方法的代码(和JDK 8一样,可能很多年都没有变过):

    for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
        put(e.getKey(), e.getValue());
    

    这是引发异常的Enumerator - 它不是prp,而是参数的Enumerator

    所以异常与prp 无关,而是与System.getProperties() 返回的Map 有关。原因是迭代系统属性映射不是线程安全的。好像有另一个线程同时在修改它。

    您需要以不同方式初始化prp。我认为clone() 是最简单的方法。

    【讨论】:

    • 这看起来可能是原因。其他人独立有相同的诊断。我已经添加了 clone(),我们将在接下来的几周内看到它是否有效。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 2010-10-10
    • 1970-01-01
    相关资源
    最近更新 更多