Генерация хэша с использованием Java-дайджеста становится медленнее

У меня действительно любопытная ситуация в рабочей среде с использованием wildfly 8.2 и Java 1.7.

Ситуация такова, что когда сервер работает более 2 недель, логин начинает снижать производительность. Я искал подсказки, которые могут указать, где проблема. Затем, проведя некоторое тестирование, я пришел к выводу, что проблема заключается в том, что пароль, вставленный в виде простого текста, зашифрован для сравнения с уже введенным.

Когда выполняется функция, которая шифрует пароль, это занимает почти 2 минуты, но при перезапуске сервера такое же выполнение занимает менее 30 секунд.

Шифрование использует java.security.MessageDigest для генерации хеша. В частности, с использованием SHA-256 с 50000 итерациями. Есть идеи, почему этот процесс может замедлиться со временем? Я использую /dev/urandom для генерации random, так что проблем не должно быть.

Вот код функции:

protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
    MessageDigest digest = getDigest(getAlgorithmName());
    if (salt != null) {
        digest.reset();
        digest.update(salt);
    }
    byte[] hashed = digest.digest(bytes);
    int iterations = hashIterations - 1; //already hashed once above
    //iterate remaining number:
    for (int i = 0; i < iterations; i++) {
        digest.reset();
        hashed = digest.digest(hashed);
    }
    return hashed;
}

3 ответа

Решение

После нескольких дней исследований я наконец нашел ответ на свой вопрос. Я хочу поделиться этим здесь в случае, если это будет полезно для кого-то еще.

Проблема была вызвана из-за кеш-памяти кода. Я сосредоточился на куче памяти и не увидел никаких проблем, но когда я проверял не кучу памяти, я обнаружил, что как только процесс входа в систему начал замедляться, в кеше кода упало более половины используемая память

Исследуя эту память, я обнаружил, что, когда в этом пространстве большие капли, может случиться так, что JIT-компилятор перестанет работать. В заключение, это было то, что происходило, и отключение JIT-компилятора приводило к тому, что каждая итерация моего цикла шифрования должна интерпретироваться при каждом выполнении, что логически делало процесс намного медленнее.

Здесь я оставляю некоторые ссылки, которые я считаю полезными в этой теме.

[1] - https://www.quora.com/In-Java-what-exactly-will-the-JVM-interpreter-and-the-JIT-compiler-do-with-the-bytecode

[2] - https://www.atlassian.com/blog/archives/codecache-is-full-compiler-has-been-disabled

Спасибо тем, кто нашел время, чтобы ответить на это в любом случае

Избавьтесь от утверждения: int iterations = hashIterations - 1; и просто использовать hashIterations,

В лучшем случае он сокращает итерации с 50000 (в указанном случае) до 49999, а в худшем случае вызывает целочисленное переполнение и увеличивает итерации до максимального значения int,

На минимальном уровне защиты от вычитания 1 когда hashIterations это ноль.

Также рассмотрите инструментарий для отладки, записав значение iterations,

Почему кто-то закроет это??? Возможно, потому что там нет ничего, что могло бы вызвать проблему.

В то время как digest.digest обычно это трудоемкая часть, это чистые вычисления, и нет ничего, что могло бы их замедлить. Так что остается getAlgorithmName() а также getDigest(String), Первый, вероятно, тривиальный добытчик, но последний, вероятно, использует MessageDigest.getInstance который находит дайджест. Просто догадываюсь: есть поиск по всем провайдерам безопасности и всему, что они предоставляют, и кто-то может как-то удлинить этот список.

Вы можете сортировать эталонные тесты этого метода библиотеки даже в производственном процессе: просто скопируйте метод в новый исходный файл и добавьте некоторое количество журналов и некоторый код, вызывающий его периодически (или вручную, если хотите). Когда произойдет замедление, у вас будет с чем сравнить, и вы найдете некоторые подробные хронометражи в своих журналах.

Когда все мыслимые причины исчерпаны, попробуйте невообразимые, например, различные iterations (который вы считаете постоянным) и т. д.

Другие вопросы по тегам