3

我正在创建一个程序,通过将文件的 MD5 与已检查的 MD5 的数据库进行比较来检查文件。

它遍历数千个文件,我看到它使用了大量内存。

如何使我的代码尽可能高效?

    for (File f : directory.listFiles()) {


        String MD5;
        //Check if the Imagefile instance is an image. If so, check if it's already in the pMap.
        if (Utils.isImage(f)) {
            MD5 = Utils.toMD5(f);
            if (!SyncFolderMapImpl.MD5Map.containsKey(MD5)) {

                System.out.println("Adding " + f.getName() + " to DB");
                add(new PhotoDTO(f.getPath(), MD5, albumName));
            }
        }

这是 toMD5:

  public static String toMD5(File file) throws IOException, NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    FileInputStream fis = new FileInputStream(file.getPath());


    byte[] dataBytes = new byte[8192];

    int nread = 0;
    while ((nread = fis.read(dataBytes)) != -1) {
        md.update(dataBytes, 0, nread);
    }

    byte[] mdbytes = md.digest();

    //convert the byte to hex format method 2
    StringBuffer hexString = new StringBuffer();
    for (int i = 0; i < mdbytes.length; i++) {
        String hex = Integer.toHexString(0xff & mdbytes[i]);
        if (hex.length() == 1) hexString.append('0');
        hexString.append(hex);
    }
    return hexString.toString();
}

编辑:尝试使用 FastMD5。结果相同。

public static String toMD5(File file) throws IOException, NoSuchAlgorithmException {

    return MD5.asHex(MD5.getHash(file));
}

编辑 2尝试使用 ThreadLocal 和 BufferedInputStream。我仍然有很多内存使用量。

private static ThreadLocal<MessageDigest> md = new ThreadLocal<MessageDigest>(){
     protected MessageDigest initialValue() {
         try {
             return MessageDigest.getInstance("MD5");
         } catch (NoSuchAlgorithmException e) {
             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
         }
         System.out.println("Fail");
         return null;

     }
};


private static ThreadLocal<byte[]> dataBytes = new ThreadLocal<byte[]>(){

    protected byte[] initialValue(){
     return new byte[1024];
    }

};

public static String toMD5(File file) throws IOException, NoSuchAlgorithmException {

    //        MessageDigest mds = md.get();
    BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));


    //        byte[] dataBytes = new byte[1024];

    int nread = 0;
    while ((nread = fis.read(dataBytes.get())) != -1) {
        md.get().update(dataBytes.get(), 0, nread);
    }

    byte[] mdbytes = md.get().digest();

    //convert the byte to hex format method 2
    StringBuffer hexString = new StringBuffer();
    fis.close();
    System.gc();
    return javax.xml.bind.DatatypeConverter.printHexBinary(mdbytes).toLowerCase();




     //        return MD5.asHex(MD5.getHash(file));
}
4

4 回答 4

4

如何使我的代码尽可能高效?

用两个词:简介它!

让您的代码工作,然后在一组典型的输入文件上运行时对其进行分析。用它来告诉你性能热点在哪里。

如果我这样做,我会首先从单线程版本开始并针对这种情况进行调整。然后我会慢慢结束线程的数量,看看性能如何扩展。一旦你达到了“最佳位置”,重做分析,看看现在的瓶颈在哪里。


实际上很难预测性能瓶颈会在哪里。这将取决于平均文件大小、您拥有的内核数量、磁盘速度以及操作系统可用于预读缓冲的内存量等因素。还有,你使用的是什么操作系统。

我的直觉是线程的数量将变得相当重要。太少,CPU 处于空闲状态,等待 I/O 系统从磁盘中获取内容。太多并且您使用额外的资源(如线程堆栈的内存)而没有真正的好处。像这样的应用程序可能会受到 I/O 限制,并且大量线程不会解决这个问题。


你是这样评论的:

性能问题纯粹是内存。我很确定我创建 MD5 哈希的方式存在问题,因此它会浪费内存。

我在您提供的代码中看不到任何会占用大量内存的内容。您生成哈希的方式没有什么大问题。AFAICT,您的代码可能导致内存使用问题的唯一方法是:

  • 您有很多很多线程都在执行该代码,或者
  • 你在内存中保留了很多很多哈希(和其他东西)。(你没有向我们展示add正在做什么。)

但我的建议是类似的,使用内存分析器并将其诊断为好像它是存储泄漏,从某种意义上说,它是!

于 2013-08-15T01:24:58.563 回答
1

快速浏览您的代码的三件事:

  • 您无需在MessageDigest每次调用该toMD5方法时都创建一个新的。每个线程一个就足够了。
  • 您无需在byte[]每次调用该toMD5方法时都创建一个新缓冲区。每个线程一个就足够了。
  • 您可能希望javax.xml.bind.DatatypeConverter.printHexBinary(byte[])用于十六进制转换。它更快。

您可以使用ThreadLocalfor each 来处理前两个项目符号。

任何进一步的优化都可能必须来自并发性。让一个线程读取文件内容,并将它们分派byte[]给不同的线程以实际计算 MD5 校验和。

于 2013-08-15T01:13:10.387 回答
0

使用更大的缓冲区,至少 8192,或者插入一个BufferedInputStream.

于 2013-08-16T01:02:47.997 回答
0

感谢人们的帮助。问题是经过的信息量如此之大,以至于 GC 无法正常工作。概念验证解决方案是在每 200 张照片之后添加一个 Thread.sleep(1000)。一个完整的解决方案是对 GC 使用更积极的方法,并一次计算散装的 MD5。

于 2013-08-18T23:15:46.010 回答