【问题标题】:Slow & memory leak when looping MD5 calculations循环 MD5 计算时缓慢和内存泄漏
【发布时间】:2013-08-23 04:50:30
【问题描述】:

我在这里Performance issues when looping an MD5 calculator on many files 提出了类似的问题,但我意识到问题不是代码,而是 GC,所以我需要一个新页面来解决同样的问题,但使用不同的解决方案。

我正在对 1000 多个图像文件运行 MD5 计算循环。该程序在大约 200 次后运行缓慢,并使用大量内存(空闲 -> 非活动)。它使用了我启动它时的可用内存 (2GB-5GB)。

我尝试使用 Java.io / FastMD5。此代码是 Java.nio 的最新测试。 所有这些都出现了问题,甚至是外部库 FastMD5,所以问题显然不是我计算 MD5 的方式。

问题似乎在于代码运行速度如此之快,以至于 GC 没有时间跟上对象。

对 GC 或我的代码是否有任何优化可以帮助我解决内存问题?

EDIT 尝试使用 ThreadLocal。不去。我意识到该程序以某种方式缓存了 MD5,因为在慢速循环(有内存泄漏)之后出现了一个没有内存泄漏的快速循环。释放内存后,程序再次变慢(然后变快)。

public static void main(String[] args) {

        File[] directory = new File("/Users/itaihay/Desktop/Library/Seattle 2010").listFiles();

    for(int i = 0; i < directory.length;i++){

        System.out.println(Utils.toMD5(directory[i]));

        }
}

Utils.toMD5():

  public class Utils {


public static final ThreadLocal<MessageDigest> mdT = new ThreadLocal<MessageDigest>(){
   protected MessageDigest initialValue(){
        try {
            return MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();  
        }
       return null;
   }
};

public static final ThreadLocal<ByteBuffer> bufferT = new ThreadLocal<ByteBuffer>(){
    protected ByteBuffer initialValue(){
            return ByteBuffer.allocate(32000);
    }
};

private static Utils util = new Utils();
private static MessageDigest md;
private static FileChannel fileChannel;
private static ByteBuffer buffer = bufferT.get();

private Utils() {

//            md = MessageDigest.getInstance("MD5");
        md = mdT.get();

}
public static String toMD5(File file) throws NoSuchAlgorithmException, IOException {
//        BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));

   RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");

    fileChannel = randomAccessFile.getChannel();

    /*while (fileChannel.read(buffer) != -1) {
        buffer.flip();
        md.update(buffer);
        buffer.clear();
    }*/

    while (fileChannel.read(bufferT.get()) != -1) {
        bufferT.get().flip();
        md.update(bufferT.get());
        bufferT.get().clear();
    }

    byte[] mdbytes = md.digest();

    randomAccessFile.close();
    bufferT.get().clear();
    mdT.get().reset();

    return javax.xml.bind.DatatypeConverter.printHexBinary(mdbytes)
            .toLowerCase();

}

【问题讨论】:

  • 您应该一遍又一遍地使用MD5 吗?每次需要时(即在每次新检查开始时)创建一个新的不是更好吗??
  • 这段代码是错误的,至少你需要在计算 md5 之前调用md.reset()。而且它肯定不是线程安全的。
  • 使用jvisualvm 确定正在使用内存的内容。我怀疑是MessageDigest,但不能确定。
  • @BevynQ 我试过 jvisualvm。这是不确定的。 MD5 被保存到内存中。不知道为什么。请参阅编辑后的帖子。尝试了您的建议。

标签: java performance memory garbage-collection md5


【解决方案1】:

@BevynQ 观察到,您的 MD5 对象正在无限增长,因为您从未重置它,而且您的代码也不是线程安全的。您必须使用方法本地 MD5ByteBuffer 对象。使用更大的ByteBuffer,例如32k 或更多,让它跑得更快。

您不需要同时关闭RandomAccessFile FileChannel:一个就可以了。

【讨论】:

  • 尝试了您的建议。不去。请参阅编辑后的帖子。该程序以某种方式将 MD5 保存到内存中,因为在一次缓慢的泄漏运行之后,快速运行却没有泄漏。
  • 你根本没有尝试过。我重复。您必须使用方法本地 MD5 和 ByteBuffer 对象。编辑后的代码仍然塞满了静态成员。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 2016-01-13
相关资源
最近更新 更多