外部排序在 readIng() 调用中给出内存不足
External Sort giving OutOfMemory in a readInt() call
我正在使用带有合并的外部排序来对整数文件进行排序。通常,解决此问题的方法是使用 -Xmx 增加 JVM 的堆大小,但我想知道是否有一种方法可以在不增加堆大小的情况下改进我的代码。
抛出错误的方法是合并方法。
public static void merge(RandomAccessFile a1, RandomAccessFile a2, RandomAccessFile b1,DataOutputStream output, int start, int end) throws FileNotFoundException, IOException {
//a1: file being read from
//b1: file being written to
if((int)a1.length() == 0) {
return;
}
DataInputStream input_a1 = new DataInputStream(
new BufferedInputStream(new FileInputStream(a1.getFD())));
DataInputStream input_a2 = new DataInputStream(
new BufferedInputStream(new FileInputStream(a2.getFD())));
b1.seek(start); //set output pointer to start
int mid = (start + end) /2;
int file_length = (int)a1.length();
//basically if second block is empty so just copy
if (end > file_length) {
end = file_length;
if (end <= mid) {
//copy from start to EOF
int no_of_ints_left = (file_length - start)/4;
for(int i = 1; i <= no_of_ints_left; i++) {
output.writeInt(input_a1.readInt());
}
output.flush();
return;
}
}
int first_counter = start;
int second_counter = mid;
int x;
int y;
while(first_counter < mid && second_counter < end) {
input_a1.mark(first_counter);
input_a2.mark(second_counter);
x = input_a1.readInt();
y = input_a2.readInt();
if(x < y) {
output.writeInt(x);
input_a2.reset();
first_counter += 4;
}
else {
output.writeInt(y);
input_a1.reset();
second_counter += 4;
}
}
output.flush();
if(first_counter == mid) {
int no_of_ints_left = (end - second_counter)/4;
while(no_of_ints_left > 0){
x = input_a2.readInt();
output.writeInt(x);
no_of_ints_left--;
}
}
else {
int no_of_ints_left = (mid - first_counter)/4;
while(no_of_ints_left > 0){
x = input_a1.readInt();
output.writeInt(x);
no_of_ints_left--;
}
}
output.flush();
input_a1 = null;
input_a2 = null;
return;
}
导致 OutOfMemory 错误的行是 x = input_a1.readInt() 行,但我怀疑这是错误的原因。我尝试在每次调用 merge() 后调用 System.gc(),但这并没有解决问题。还有什么其他方法可以优化该方法以使其使用更少的内存?
代码在缓冲流上使用 mark
。这将需要整个标记的部分都在内存中,这可能是你的问题。
RandomAccessFile
可能是获得相同功能但将数据保存在磁盘上的最简单方法。使用 java.nio
的内存映射是一种更复杂的技术 - 在 32 位机器上仍然可以 运行 解决问题。但是,我猜您仍然可能 运行 遇到大文件的严重性能问题。
(堆栈跟踪对于准确查看分配失败的内容也很有用。可能会重新分配缓冲区。尽管分析堆会告诉您实际占用内存的内容。)
我正在使用带有合并的外部排序来对整数文件进行排序。通常,解决此问题的方法是使用 -Xmx 增加 JVM 的堆大小,但我想知道是否有一种方法可以在不增加堆大小的情况下改进我的代码。
抛出错误的方法是合并方法。
public static void merge(RandomAccessFile a1, RandomAccessFile a2, RandomAccessFile b1,DataOutputStream output, int start, int end) throws FileNotFoundException, IOException {
//a1: file being read from
//b1: file being written to
if((int)a1.length() == 0) {
return;
}
DataInputStream input_a1 = new DataInputStream(
new BufferedInputStream(new FileInputStream(a1.getFD())));
DataInputStream input_a2 = new DataInputStream(
new BufferedInputStream(new FileInputStream(a2.getFD())));
b1.seek(start); //set output pointer to start
int mid = (start + end) /2;
int file_length = (int)a1.length();
//basically if second block is empty so just copy
if (end > file_length) {
end = file_length;
if (end <= mid) {
//copy from start to EOF
int no_of_ints_left = (file_length - start)/4;
for(int i = 1; i <= no_of_ints_left; i++) {
output.writeInt(input_a1.readInt());
}
output.flush();
return;
}
}
int first_counter = start;
int second_counter = mid;
int x;
int y;
while(first_counter < mid && second_counter < end) {
input_a1.mark(first_counter);
input_a2.mark(second_counter);
x = input_a1.readInt();
y = input_a2.readInt();
if(x < y) {
output.writeInt(x);
input_a2.reset();
first_counter += 4;
}
else {
output.writeInt(y);
input_a1.reset();
second_counter += 4;
}
}
output.flush();
if(first_counter == mid) {
int no_of_ints_left = (end - second_counter)/4;
while(no_of_ints_left > 0){
x = input_a2.readInt();
output.writeInt(x);
no_of_ints_left--;
}
}
else {
int no_of_ints_left = (mid - first_counter)/4;
while(no_of_ints_left > 0){
x = input_a1.readInt();
output.writeInt(x);
no_of_ints_left--;
}
}
output.flush();
input_a1 = null;
input_a2 = null;
return;
}
导致 OutOfMemory 错误的行是 x = input_a1.readInt() 行,但我怀疑这是错误的原因。我尝试在每次调用 merge() 后调用 System.gc(),但这并没有解决问题。还有什么其他方法可以优化该方法以使其使用更少的内存?
代码在缓冲流上使用 mark
。这将需要整个标记的部分都在内存中,这可能是你的问题。
RandomAccessFile
可能是获得相同功能但将数据保存在磁盘上的最简单方法。使用 java.nio
的内存映射是一种更复杂的技术 - 在 32 位机器上仍然可以 运行 解决问题。但是,我猜您仍然可能 运行 遇到大文件的严重性能问题。
(堆栈跟踪对于准确查看分配失败的内容也很有用。可能会重新分配缓冲区。尽管分析堆会告诉您实际占用内存的内容。)