在 Java 中读取大文件时如何避免 OutOfMemory 异常
How to avoid OutOfMemory exception while reading large files in Java
我正在开发从文件中读取大量数据的应用程序。基本上,我有一个巨大的文件(大约 1.5 - 2 gigs)包含不同的对象(每个文件约 5 到 1000 万个)。我需要阅读所有这些并将它们放在应用程序中的不同地图上。问题是应用程序在某个时候读取对象时内存不足。只有当我将它设置为使用 -Xmx4096m - 它才能处理该文件。但是如果文件变大,它就不能再这样做了。
这是代码片段:
String sampleFileName = "sample.file";
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try{
fileInputStream = new FileInputStream(new File(sampleFileName));
int bufferSize = 16 * 1024;
objectInputStream = new ObjectInputStream(new BufferedInputStream(fileInputStream, bufferSize));
while (true){
try{
Object objectToRead = objectInputStream.readUnshared();
if (objectToRead == null){
break;
}
// doing something with the object
}catch (EOFException eofe){
eofe.printStackTrace();
break;
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
} catch (Exception e){
e.printStackTrace();
}finally{
if (objectInputStream != null){
try{
objectInputStream.close();
}catch (Exception e2){
e2.printStackTrace();
}
}
if (fileInputStream != null){
try{
fileInputStream.close();
}catch (Exception e2){
e2.printStackTrace();
}
}
}
首先,我使用的是 objectInputStream.readObject() 而不是 objectInputStream.readUnshared(),所以它部分解决了这个问题。当我将内存从 2048 增加到 4096 时,它开始解析文件。 BufferedInputStream 已在使用中。从网络上我只找到了如何读取行或字节的示例,但没有关于对象、性能方面的信息。
如何在不增加JVM内存和避免OutOfMemory异常的情况下读取文件?有没有办法从文件中读取对象,而不是在内存中保留任何其他内容?
当读取大文件、解析对象并将它们保存在内存中时,有多种解决方案需要权衡取舍:
对于部署在一台服务器上的应用程序,您可以将所有已解析的对象放入内存中。它要么需要以非常压缩的方式存储所有对象,例如使用字节或整数来存储 2 个数字,或者在其他数据结构中进行某种移位。换句话说,以可能的最小值 space 拟合所有对象。或者增加该服务器的内存(垂直缩放)
a) 但是读取文件会占用太多内存,因此您必须分块读取它们。例如,这就是我对 json 个文件所做的:
JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
if (reader.hasNext()) {
reader.beginObject();
String name = reader.nextName();
if ("content".equals(name)) {
reader.beginArray();
parseContentJsonArray(reader, name2ContentMap);
reader.endArray();
}
name = reader.nextName();
if ("ad".equals(name)) {
reader.beginArray();
parsePrerollJsonArray(reader, prerollMap);
reader.endArray();
}
}
想法是有一种方法来识别某个对象何时开始和结束,并只读取该部分。
b) 如果可以的话,你也可以在源文件中将文件拆分成更小的文件,这样阅读起来会更容易。
您无法在一台服务器上容纳该应用程序的所有已解析对象。在这种情况下,您必须基于某个对象 属性 进行分片。例如将基于美国州的数据拆分到多个服务器中。
希望对您的解决方案有所帮助。
我正在开发从文件中读取大量数据的应用程序。基本上,我有一个巨大的文件(大约 1.5 - 2 gigs)包含不同的对象(每个文件约 5 到 1000 万个)。我需要阅读所有这些并将它们放在应用程序中的不同地图上。问题是应用程序在某个时候读取对象时内存不足。只有当我将它设置为使用 -Xmx4096m - 它才能处理该文件。但是如果文件变大,它就不能再这样做了。
这是代码片段:
String sampleFileName = "sample.file";
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try{
fileInputStream = new FileInputStream(new File(sampleFileName));
int bufferSize = 16 * 1024;
objectInputStream = new ObjectInputStream(new BufferedInputStream(fileInputStream, bufferSize));
while (true){
try{
Object objectToRead = objectInputStream.readUnshared();
if (objectToRead == null){
break;
}
// doing something with the object
}catch (EOFException eofe){
eofe.printStackTrace();
break;
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
} catch (Exception e){
e.printStackTrace();
}finally{
if (objectInputStream != null){
try{
objectInputStream.close();
}catch (Exception e2){
e2.printStackTrace();
}
}
if (fileInputStream != null){
try{
fileInputStream.close();
}catch (Exception e2){
e2.printStackTrace();
}
}
}
首先,我使用的是 objectInputStream.readObject() 而不是 objectInputStream.readUnshared(),所以它部分解决了这个问题。当我将内存从 2048 增加到 4096 时,它开始解析文件。 BufferedInputStream 已在使用中。从网络上我只找到了如何读取行或字节的示例,但没有关于对象、性能方面的信息。
如何在不增加JVM内存和避免OutOfMemory异常的情况下读取文件?有没有办法从文件中读取对象,而不是在内存中保留任何其他内容?
当读取大文件、解析对象并将它们保存在内存中时,有多种解决方案需要权衡取舍:
对于部署在一台服务器上的应用程序,您可以将所有已解析的对象放入内存中。它要么需要以非常压缩的方式存储所有对象,例如使用字节或整数来存储 2 个数字,或者在其他数据结构中进行某种移位。换句话说,以可能的最小值 space 拟合所有对象。或者增加该服务器的内存(垂直缩放)
a) 但是读取文件会占用太多内存,因此您必须分块读取它们。例如,这就是我对 json 个文件所做的:
JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); if (reader.hasNext()) { reader.beginObject(); String name = reader.nextName(); if ("content".equals(name)) { reader.beginArray(); parseContentJsonArray(reader, name2ContentMap); reader.endArray(); } name = reader.nextName(); if ("ad".equals(name)) { reader.beginArray(); parsePrerollJsonArray(reader, prerollMap); reader.endArray(); } }
想法是有一种方法来识别某个对象何时开始和结束,并只读取该部分。
b) 如果可以的话,你也可以在源文件中将文件拆分成更小的文件,这样阅读起来会更容易。
您无法在一台服务器上容纳该应用程序的所有已解析对象。在这种情况下,您必须基于某个对象 属性 进行分片。例如将基于美国州的数据拆分到多个服务器中。
希望对您的解决方案有所帮助。