пример утечки памяти загрузчика классов java

#java #tomcat

Вопрос:

Я нашел очень интересный пример утечки памяти.

 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 примерно так <% Misc.getDbData ()%>, а в Misc вместо sql у меня есть MessageResources sqlResources. После развертывания иногда 5 минут не работает нормально.(Я получил ошибку, подобную этой java.sql.SQLException: символ, маркер или предложение недопустимы или отсутствуют)

Я думаю, что это может быть проблема выше с загрузчиком классов, но tomcat в конце концов решает проблему.

возможные решения:

  1. преобразуйте класс Misc в гигантский(у меня здесь несколько конфигураций) синглтон, и мне придется провести рефакторинг более чем в 10000 местах.
  2. отделение констант класса от разных, но у меня все еще есть другое свойство, основанное на версии системы.
  3. Перемещение Misc.getDbData() из jsp в действие было бы временным решением для этого? как работает поток для jsp/действия?
  4. у вас есть другие решения на примете?

Ответ №1:

Во-первых, это не имеет никакого отношения к утечке памяти.

Что касается решения, вам , вероятно, следует просто выполнить ленивую инициализацию 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 , GetDBData , Constants .
  • В потоке t2 порядок загрузки Constants равен, Misc , GetDBData .

Теперь это условие гонки: если один из потоков завершит инициализацию всех 3 до того, как туда доберется другой поток, то проблем не возникнет.

Если они работают параллельно:

  1. t загрузится/заблокируется Misc и t2 загрузится/заблокируется Constants
  2. t загрузится/заблокируется GetDBData и t2 будет ждать Misc
  3. t будет ждать дальше Constants

Теперь нити зашли в тупик.

Комментарии:

1. Я перенесу весь код загрузки БД в общедоступные статические синхронизированные методы получения для ленивой инициализации. и я позабочусь о том, чтобы геттер использовался для получения данных, меняющих общедоступную статическую строку dbData; на частную. В любом случае, я не понимаю, почему мой представленный код иногда блокируется, а иногда работает и как tomcat успешно восстанавливается после этого.