#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 в конце концов решает проблему.
возможные решения:
- преобразуйте класс Misc в гигантский(у меня здесь несколько конфигураций) синглтон, и мне придется провести рефакторинг более чем в 10000 местах.
- отделение констант класса от разных, но у меня все еще есть другое свойство, основанное на версии системы.
- Перемещение Misc.getDbData() из jsp в действие было бы временным решением для этого? как работает поток для jsp/действия?
- у вас есть другие решения на примете?
Ответ №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 до того, как туда доберется другой поток, то проблем не возникнет.
Если они работают параллельно:
t
загрузится/заблокируетсяMisc
иt2
загрузится/заблокируетсяConstants
t
загрузится/заблокируетсяGetDBData
иt2
будет ждатьMisc
t
будет ждать дальшеConstants
Теперь нити зашли в тупик.
Комментарии:
1. Я перенесу весь код загрузки БД в общедоступные статические синхронизированные методы получения для ленивой инициализации. и я позабочусь о том, чтобы геттер использовался для получения данных, меняющих общедоступную статическую строку dbData; на частную. В любом случае, я не понимаю, почему мой представленный код иногда блокируется, а иногда работает и как tomcat успешно восстанавливается после этого.