Загрузка KeyStore в Java 7 утечки Classloader

При загрузке KeyStore в Java 7 просачивается Classloader.

Я подтвердил это, используя функцию "Найти утечки" в Tomcat 7.0.47 и classloader-leak-prevention. Вот тестовый код, webapp с утечкой в ​​@Configuration и webapp с утечкой в ​​@Controller.

По существу эти строки вызывают утечку для меня:

InputStream is = null;
try {
    is = new FileInputStream("./app.truststore");
    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(is, "changeit".toCharArray());
} catch (Exception e) {
    System.out.println(e);
} finally {
    if (is != null) {
        is.close();
    }
}

Если я удалю KeyStore.load(), все работает нормально, но это, очевидно, не работает.

Он не работает на Oracle JDK 1.7u15, u17, u21, u25, u40 и u45, а также OpenJDK 1.7u40 и u45.

Он работает на Oracle JDK 1.6u39, u41, u43 и 45, а также OpenJDK 1.6.0.

Это было протестировано на Microsoft Windows Server 2008 R2 Standard 64 Bit. OpenJDKs являются последними неофициальные сборки alexkasko на GitHub.

Есть ли у кого-нибудь идея, что может вызвать утечку Classloader? Я попытался использовать кучу кучи и назвал "самый короткий путь к GC root", но это не дало никаких результатов.

Ответ 1

Класс Loader не течет. При развертывании пустых пустых приложений и PermGen достигает определенного порога Tomcat Find Leaks прекращает сообщать о приложении. Таким образом, это был ложный позитив.

Ответ 2

Я пересматривал это с помощью нескольких разных подходов. Я взял вашу логику и бросил ее в webapp. Первый, который я пробовал, использовал Spring MVC с Gemfire, и я добавил ваш код в инициализатор веб-приложения. При тестировании этого кода на eval коммерческого инструмента под названием Plumbr он обнаружил утечку загрузчика классов. Однако я не смог увидеть отчет в eval.

Люди Plumbr связались со мной и предоставили мне отчет, и я мог видеть, что Gemfire оказался корнем проблемы. Я использовал сеансы Gemfire и удаленное кэширование, время от времени он не закрывается изящно и в этом случае появляется как утечка.

Затем я подрезал все обратно к простейшему webapp, 1 Spring и 1 bean с логикой, полученной из вашего кода, следующим образом:

public class ClassloaderLeakingBean {

    private static Logger logger = LoggerFactory.getLogger(ClassloaderLeakingBean.class);
    public void initiateLeak(String trustStore) throws Exception {
        InputStream is = null;
        try {
            is = new FileInputStream(trustStore);
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(is, "password".toCharArray());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (is != null) {
                is.close();
                logger.info("The Input Stream is Closed!");
            }
        }

    }
}

Затем я выбросил bean в контроллер, который просто вызвал метод initiateLeak в ответ на запрос GET. Эта упрощенная версия не обнаружила признаков утечки загрузчика, независимо от того, что я на нее набрасываю.

Версия java runtime выглядит следующим образом:

java version "1.7.0" Java (TM) SE Runtime Environment (build 1.7.0-b147) 64-разрядная виртуальная машина Java HotSpot TM (сборка 21.0-b17, смешанный режим)

Я скомпилировал код с использованием maven с источником и целевым значением 1.7.

Я собираюсь исследовать это дальше как автономное приложение, но я не видел никаких сообщений об утечках классов для Java 7 и класса KeyStore.

Следует иметь в виду: я работаю на 64-битной JVM, Windows 7 Enterprise, которая может действовать иначе, чем 32-битная среда. Я лично избегаю неофициальных сборок при поиске утечек памяти.