Всякий раз, когда я использую LDAP в веб-приложении, это вызывает утечку загрузчика класса, и странная вещь - профилировщики не находят никаких корней GC.Утечка памяти LDAP PermGen
Я создал простой веб-приложение, демонстрирующее утечку, она включает в себя только этот класс:
@WebListener
public class LDAPLeakDemo implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
useLDAP();
}
public void contextDestroyed(ServletContextEvent sce) {}
private void useLDAP() {
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ldap.forumsys.com:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=read-only-admin,dc=example,dc=com");
env.put(Context.SECURITY_CREDENTIALS, "password");
try {
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
System.out.println("Created the initial context");
} finally {
if (ctx != null) {
ctx.close();
System.out.println("Closed the context");
}
}
} catch (NamingException e) {
e.printStackTrace();
}
}
}
Исходный код доступен here. Я использую a public LDAP test server для этого примера, поэтому он должен работать для всех, если вы хотите попробовать. Я попробовал это с последними версиями JDK 7 и 8 и Tomcat 7 и 8 с таким же результатом - когда я нажимаю «Обновить» в Tomcat Web Application Manager, а затем «Найти утечки», Tomcat сообщает, что есть утечка, и профайлеры подтверждают это.
В этом примере утечка едва заметна, но она вызывает OutOfMemory в большом веб-приложении. Я не нашел никаких открытых ошибок JDK.
UPDATE 1
Я пытался использовать Jetty 9.2 вместо Tomcat, и я до сих пор вижу утечку, так это не вина Tomcat. Либо это ошибка JDK, либо я делаю что-то неправильно.
UPDATE 2
Хотя мой пример демонстрирует утечку, он не демонстрирует отказ от ошибки памяти, поскольку он имеет очень маленький PermGen след. Я создал another branch, который должен иметь возможность воспроизводить OutOfMemoryError. Я просто добавил зависимости Spring, Hibernate и Logback к проекту, чтобы увеличить потребление PermGen. Эти зависимости не имеют ничего общего с утечкой, и я мог бы использовать любые другие. Единственная цель - сделать потребление PermGen достаточно большим, чтобы получить OutOfMemoryError.
Действия по воспроизведению OutOfMemoryError:
Скачать или клонировать outofmemory-demo branch.
Убедитесь, что у вас есть JDK 7 и любая версия Tomcat и Maven (я использовал последние версии - JDK 1.7.0_79 и Tomcat 8.0.26).
Уменьшите размер PermGen, чтобы увидеть сообщение об ошибке после первой перезагрузки. Создайте setenv.bat (Windows) или setenv.sh (Linux) в каталоге bin Tomcat и добавьте
set "JAVA_OPTS=-XX:PermSize=24m -XX:MaxPermSize=24m"
(Windows) илиexport "JAVA_OPTS=-XX:PermSize=24m -XX:MaxPermSize=24m"
(Linux).Перейдите в каталог confc Tomcat, откройте tomcat-users.xml и добавьте
<role rolename="manager-gui"/><user username="admin" password="1" roles="manager-gui"/>
внутри<tomcat-users></ tomcat-users>
, чтобы иметь возможность использовать Tomcat Web Application Manager.Перейдите в каталог проекта и используйте
mvn package
, чтобы построить .war.Перейдите в каталог webapps Tomcat, удалите все, кроме каталога менеджера, и скопируйте здесь .war здесь.
Run для Tomcat сценарий запуска (бен \ startup.bat или бен/startup.sh) и открытым http://localhost:8080/manager/, используйте имя пользователя и пароль администратора 1.
Кликните на Reload, и вы должны увидеть java.lang.OutOfMemoryError: пространство PermGen в консоли Tomcat.
Остановить Tomcat, открыть исходный файл проекта
src\main\java\org\example\LDAPLeakDemo.java
, удалитьuseLDAP();
и сохранить его.Повторите шаги 5-8, только на этот раз нет OutOfMemoryError, потому что код LDAP никогда не вызывается.
Tomcat сообщает, что именно? – EJP
@EJP Это стандартное сообщение об утечке - «Следующие веб-приложения были остановлены (перезагружены, не развернуты), но их классы из предыдущих запусков по-прежнему загружаются в память, что вызывает утечку памяти (используйте профилировщик для подтверждения)». – John29
Это из-за предшествующего состояния. Вы должны показать нам это. Сохранение классов в памяти - это не утечка памяти в течение длительного времени. Это нормально и существенно. Ваша растущая проблема памяти лежит где-то в другом месте. – EJP