首页 > 解决方案 > java类加载器内存泄漏示例

问题描述

我发现了一个非常有趣的内存泄漏示例。

public class Misc {

    public static final String sql;
    public static String dbData;

    static {
        System.out.println("static started");
        sql = "select";
        dbData = GetDBData.execute();
        System.out.println("static ended");
    }

    public static String getDbData() {
        System.out.println("getDbData");
        return dbData;
    }
}

public class GetDBData {

    private String sql = Constants.mysql;

    public String getMessage(){
        System.out.println("getMessage");
        if ("select".equalsIgnoreCase(sql))
            return "dbData";
        return "nodata";
    }

    public static String execute(){
        System.out.println("execute");
        GetDBData myInst = new GetDBData();

        return myInst.getMessage();
    }
}

public class Constants {
    public static final String mysql = Misc.sql;
}

public class Main {

    static Thread t = new Thread() {
        @Override
        public void run() {
            Thread.currentThread().setName("GETDATA THREAD1");
            System.out.println("--------" + Thread.currentThread().getName() + " started");
            System.out.println("--------" + Thread.currentThread().getName() + "-" + Misc.getDbData());
        }
    };

    static Thread t2 = new Thread() {
        @Override
        public void run() {
            Thread.currentThread().setName("Constants THREAD");
            System.out.println("--------" + Thread.currentThread().getName() + " started");
            System.out.println("--------" + Thread.currentThread().getName() + "-" + Constants.mysql);
        }
    };

    public static void main(String[] args) {
        t.start();
        t2.start();
    }
}

如果您多次运行代码,则代码会阻塞在 Misc 类的静态块上,但有时会通过并执行完成。

在我基于 struts 的应用程序代码中,我有一个 jsp 文件,它正在调用诸如 <% Misc.getDbData() %> 之类的 Misc 类,而在 Misc 而不是 sql 中,我有 MessageResources sqlResources。部署后有时 5 分钟无法正常工作。(我收到类似 java.sql.SQLException 的错误:字符、令牌或子句无效或丢失)

我在想,这可能是上面类加载器的问题,但 tomcat 最终设法解决了这个问题。

可能的解决方案:

  1. 在一个巨人(我这里有多种配置)单例中转换 Misc 类,我必须在 10000 多个地方进行重构。
  2. 将类常量从杂项中解耦,但我还有另一个基于系统版本的属性。
  3. 将 Misc.getDbData() 从 jsp 转移到行动中会是一个临时解决方案吗?jsp/action 的线程是如何工作的?
  4. 您有其他解决方案吗?

标签: javatomcat

解决方案


首先,这与内存泄漏无关。

至于解决方案,您可能应该只对 进行延迟初始化dbData,因此可以在创建其中一个实例之前初始化所有类。

public class Misc {

    public static final String sql;
    public static String dbData;

    static {
        System.out.println("static started");
        sql = "select";
        System.out.println("static ended");
    }

    public static synchronized String getDbData() {
        System.out.println("getDbData");
        if (dbData == null) {
            dbData = GetDBData.execute();
        }
        return dbData;
    }
}

更新:问题代码问题的解释。

当第一次需要一个类时,它将被加载到内存中,半格式的类被锁定,并且静态初始化程序开始执行。在此期间,无法从另一个线程访问该类。

问题代码启动2个线程:

  • 在线程中,t类按顺序加载Misc,,,。GetDBDataConstants
  • 在线程t2中,加载顺序是Constants, Misc, GetDBData.

现在是一个竞争条件:如果一个线程在另一个线程到达那里之前完成了所有 3 个线程的初始化,那么就没有问题。

如果它们并行运行:

  1. t将加载/锁定Misc并将t2加载/锁定Constants
  2. t将加载/锁定GetDBDatat2等待Misc
  3. t将等待Constants

线程现在已死锁


推荐阅读