我写了一个文件复制处理器,它获取每个文件的MD5哈希值,将其添加到一个哈希Map中,然后获取所有具有相同哈希值的文件,并将其添加到一个名为dupeList的哈希Map中。
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.nio.file.Files.read(Unknown Source)
at java.nio.file.Files.readAllBytes(Unknown Source)
at com.embah.FileDupe.Utils.FileUtils.getMD5Hash(FileUtils.java:14)
at com.embah.FileDupe.FileDupe.getDuplicateFiles(FileDupe.java:43)
at com.embah.FileDupe.FileDupe.getDuplicateFiles(FileDupe.java:68)
at ImgHandler.main(ImgHandler.java:14)
我敢肯定这是由于它处理这么多文件的事实,但我不知道一个更好的方式来处理它。我试图让这个工作,这样我就可以筛选通过所有我的孩子的婴儿照片,并删除重复之前,我把他们放在我的外部硬盘驱动器长期存储。感谢大家的帮助!
我的代码
public class FileUtils {
public static String getMD5Hash(String path){
try {
byte[] bytes = Files.readAllBytes(Paths.get(path)); //LINE STACK THROWS ERROR
byte[] hash = MessageDigest.getInstance("MD5").digest(bytes);
bytes = null;
String hexHash = DatatypeConverter.printHexBinary(hash);
hash = null;
return hexHash;
} catch(Exception e){
System.out.println("Having problem with file: " + path);
return null;
}
}
public class FileDupe {
public static Map<String, List<String>> getDuplicateFiles(String dirs){
Map<String, List<String>> allEntrys = new HashMap<>(); //<hash, file loc>
Map<String, List<String>> dupeEntrys = new HashMap<>();
File fileDir = new File(dirs);
if(fileDir.isDirectory()){
ArrayList<File> nestedFiles = getNestedFiles(fileDir.listFiles());
File[] fileList = new File[nestedFiles.size()];
fileList = nestedFiles.toArray(fileList);
for(File file:fileList){
String path = file.getAbsolutePath();
String hash = "";
if((hash = FileUtils.getMD5Hash(path)) == null)
continue;
if(!allEntrys.containsValue(path))
put(allEntrys, hash, path);
}
fileList = null;
}
allEntrys.forEach((hash, locs) -> {
if(locs.size() > 1){
dupeEntrys.put(hash, locs);
}
});
allEntrys = null;
return dupeEntrys;
}
public static Map<String, List<String>> getDuplicateFiles(String... dirs){
ArrayList<Map<String, List<String>>> maps = new ArrayList<Map<String, List<String>>>();
Map<String, List<String>> dupeMap = new HashMap<>();
for(String dir : dirs){ //Get all dupe files
maps.add(getDuplicateFiles(dir));
}
for(Map<String, List<String>> map : maps){ //iterate thru each map, and add all items not in the dupemap to it
dupeMap.putAll(map);
}
return dupeMap;
}
protected static ArrayList<File> getNestedFiles(File[] fileDir){
ArrayList<File> files = new ArrayList<File>();
return getNestedFiles(fileDir, files);
}
protected static ArrayList<File> getNestedFiles(File[] fileDir, ArrayList<File> allFiles){
for(File file:fileDir){
if(file.isDirectory()){
getNestedFiles(file.listFiles(), allFiles);
} else {
allFiles.add(file);
}
}
return allFiles;
}
protected static <KEY, VALUE> void put(Map<KEY, List<VALUE>> map, KEY key, VALUE value) {
map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);
}
public class ImgHandler {
private static Scanner s = new Scanner(System.in);
public static void main(String[] args){
System.out.print("Please enter locations to scan for dupelicates\nSeperate Location via semi-colon(;)\nLocations: ");
String[] locList = s.nextLine().split(";");
Map<String, List<String>> dupes = FileDupe.getDuplicateFiles(locList);
System.out.println(dupes.size() + " dupes detected!");
dupes.forEach((hash, locs) -> {
System.out.println("Hash: " + hash);
locs.forEach((loc) -> System.out.println("\tLocation: " + loc));
});
}
5条答案
按热度按时间zkure5ic1#
将整个文件读取到字节数组中不仅需要足够的堆空间,而且还限制文件大小最大为
Integer.MAX_VALUE
* 原则上 *(HotSpot JVM的实际限制甚至要小几个字节)。最好的解决方案是根本不将数据加载到堆内存中:
如果底层的
MessageDigest
实现是纯Java实现,它会将数据从直接缓冲区传输到堆,但这超出了您的职责范围(并且这将是所消耗的堆内存和性能之间的合理权衡)。上面的方法将处理超过2GiB大小的文件而不会出现问题。
6rqinv9w2#
无论
FileUtils
有什么实现,它都试图读入整个文件来计算哈希值。这是不必要的:通过阅读更小的块来进行计算是可能的。事实上,要求这样做是一种糟糕的设计,而不是简单地读取所需的块(64字节?)。因此,也许你需要使用一个更好的库。q9yhzks03#
你有很多解决方案:
1.不要一次读取所有字节,尽量使用
BufferedInputStream
,并且每次读取大量字节.但不是所有文件。1.使用C/C++来做这样的事情,(嗯,这是不安全的,因为你要自己处理内存)
pbossiut4#
考虑使用Guava:
Guava将为您缓冲文件的阅读。
9avjhtql5#
我在我的Windows机器上有这个java堆空间错误,我花了几个星期在网上搜索解决方案,我试图增加我的-Xmx值更高,但没有成功。我甚至尝试运行我的spring Boot 应用程序与一个参数,以增加堆大小在运行时与命令,如下面一个
但仍然没有成功。直到我发现我运行的是jdk 32位,它的内存大小有限,我不得不卸载32位,安装64位,这为我解决了我的问题。我希望这能帮助有类似问题的人。