使用NIO的内存映射计算超大文件的MD5

综合技术 2017-07-09

在最近的开发及原有方案的改良中,一个feture就是加快对GB级大文件的读取和计算MD5的速度。这是一个IO密集和CPU密集的耗时操作,

在无法硬性提高CPU的条件下,我考虑从IO上如何提高速率。

  1. 超大文件的MD5计算,需要分段将文件中的内存更新到MessageDigest中。(注:MessageDigest的实例不能共享,CSDN等博客上介绍MD5计算的demo,将MessageDigest设置为单例模式,单线程计算一个文件的MD5不会出错,多线程计算就会出问题了。)
  2. Java的NIO中提供了内存映射,通过将文件的一部分映射到内存中,可以一定程度地提高IO速率,从提高整体的效率。使用NIO的内存映射需要注意
    内存的释放(之前未释放内存,在100GB级的文件测试中,抛出了OOM错误)。

分段计算MD5的代码实现如下:

public static byte[] getMD5Digits(File file) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        FileChannel channel = inputStream.getChannel();
        try {
            MessageDigest messagedigest = MessageDigest.getInstance("MD5");
            long position = 0;
            long remaining = file.length();
            MappedByteBuffer byteBuffer = null;
            while (remaining > 0) {
                long size = Integer.MAX_VALUE / 2;
                if (size > remaining) {
                    size = remaining;
                }
                byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, position, size);
                messagedigest.update(byteBuffer);
                position += size;
                remaining -= size;
            }
            unMapBuffer(byteBuffer, channel.getClass());
            return messagedigest.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } finally {
            channel.close();
            inputStream.close();
        }
    }

手动释放映射内存的实现如下:

/**
 * JDK不提供MappedByteBuffer的释放,但是MappedByteBuffer在Full GC时才被回收,通过手动释放的方式让其回收
 *
 * @param buffer
 */
public static void unMapBuffer(MappedByteBuffer buffer, Class channelClass)throwsIOException{
    if (buffer == null) {
        return;
    }

    Throwable throwable = null;
    try {
        Method unmap = channelClass.getDeclaredMethod("unmap", MappedByteBuffer.class);
        unmap.setAccessible(true);
        unmap.invoke(channelClass, buffer);
    } catch (NoSuchMethodException e) {
        throwable = e;
    } catch (IllegalAccessException e) {
        throwable = e;
    } catch (InvocationTargetException e) {
        throwable = e;
    }

    if (throwable != null) {
        throw new IOException("MappedByte buffer unmap error", throwable);
    }
}

您可能感兴趣的

【技术分享】现代无线鼠标及键盘的安全性分析报告... 2017-06-15 15:27:14 阅读:85次 来源: exploit-db.com 作者:興趣使然的小胃 翻译: 興趣使然的小胃 预估 稿费:300RMB 投稿方式:发送邮件...
linux中生成考核用的FAT32文件系统结构样例... 实验X说明:FAT32-1.img是一个包含FAT32文件系统的磁盘镜像,请使用winhex手工方式读出这个文件系统内的指定文件,并回答其md5 HASH值。 要求: 1、利用WINHEX手工方式读取。 2、不得使用WINHEX模板功能。 3、不得使用WINHEX文件系统解...
AES对称加密 作者: tiankonguse | 更新日期: 2017-05-17 23:35:00 简单介绍一下AES对称加密算法。 大家好,这里是tiankonguse的公众号(tiankonguse-code)。 tiankonguse曾是一名ACMer,现在是鹅厂视频部门的后台开...
Petya Variant Sample Technical Analysis 阅读: 79 On the evening of June 27, 2017, multiple enterprises were attacked by ransomware, hence service interruption was caused. The first in...
你以为Petya真的是勒索软件吗?背后可能是一次国家级攻击... 最近两天, Petya勒索软件席卷欧洲 ,包括乌克兰首都基辅的鲍里斯波尔国际机场、乌克兰国家储蓄银行、船舶公司、俄罗斯石油公司和乌克兰一些商业银行以及部分私人公司、零售企业和政府系统都遭到了攻击,Petya波及的国家还包括英国、印度、荷兰、西班牙、丹麦等。 但研究人员的最新研究结果...