4

在使用 wildfly 8.2 和 Java 1.7 的生产环境中,我遇到了一个非常奇怪的情况。

情况是,当服务器启动超过 2 周时,登录开始降低性能。我一直在寻找可能表明问题所在的线索。然后在做了一些测试后,我得出的结论是,问题是当以明文形式插入的密码被加密以与已经插入的密码进行比较时。

当执行加密密码的函数时,它需要将近 2 分钟,但当服务器重新启动时,相同的执行时间不到 30 秒。

加密使用 java.security.MessageDigest 生成哈希。特别是使用 SHA-256 进行 50000 次迭代。知道为什么这个过程会随着时间的推移而变慢吗?我正在使用 /dev/urandom 来生成随机数,所以这不应该是问题。

下面是函数代码:

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;
}
4

3 回答 3

5

经过几天的研究,我终于找到了我的问题的答案。我想在这里分享它,以防它对其他人有用。

该问题是由于代码高速缓存内存引起的。我专注于堆内存并没有发现任何问题,但是当我检查非堆内存时,我发现就在登录过程开始变慢时,代码缓存下降了一半以上使用的内存。

调查这个内存,我发现当这个空间有很大的下降时,可能会发生 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

感谢那些花时间回答它的人

于 2017-06-20T10:48:32.780 回答
0

摆脱声明:int iterations = hashIterations - 1;并使用hashIterations.

在最好的情况下,它将迭代次数从 50000(在所述情况下)减少到 49999,在最坏的情况下,会导致整数下溢并将迭代次数增加到 的最大值int

至少要防止在为零1时进行减法。hashIterations

还可以通过记录iterations.

于 2017-06-14T12:00:30.583 回答
0

为什么有人会关闭这个???也许是因为那里没有任何可能导致问题的东西。

虽然digest.digest通常是耗时的部分,但它是一个纯粹的计算,没有什么可以减慢它的速度。所以剩下的是getAlgorithmName()getDigest(String)。前者可能是一个微不足道的吸气剂,但后者可能使用MessageDigest.getInstance它来定位摘要。只是猜测:对所有安全提供商及其提供的所有东西都有一个查找,有人可能会以某种方式延长这个列表。

您甚至可以在生产中对这个库方法进行某种基准测试:只需将该方法复制到一个新的源文件中并添加一些日志记录和一些定期调用它的代码(或手动,如果您愿意)。当减速发生时,您将有一些可比较的东西,并且您会在日志中找到一些详细的时间安排。

当所有可以想象的原因都用尽时,尝试无法想象的原因,例如变化iterations(您认为是常数)等。

于 2017-06-14T23:36:11.100 回答