NIO MappedByteBuffer関数を使用して非常に大きな地震探査ファイルを読み取る際に問題があります。私のプログラムが読み取るフォーマットはSEGYと呼ばれ、地震データのサンプルと、他の項目の中でも、地震データの数値IDとXY座標に関するメタデータで構成されています。MappedByteBufferがメモリを解放しない
フォーマットの構造は、各地震トレースを構成する固定数のデータサンプルが続く240バイトのヘッダーでかなり固定されています。トレースごとのサンプル数はファイルごとに異なる場合がありますが、通常は1000〜2000程度です。
サンプルは、シングルバイト、16または32ビットの整数、IBMまたはIEEEのfloatのいずれかとして記述できます。各トレースヘッダー内のデータは、同様に上記の形式のいずれかになります。この問題をさらに混乱させるために、SEGYファイルはビッグエンディアンまたはリトルエンディアンのバイトオーダーにすることができます。
ファイルのサイズは、3600バイトから数テラバイトまでです。
私のアプリケーションはSEGYエディタとビューアです。実行する関数の多くは、1つまたは2つの変数、たとえば各トレースヘッダーからlong intを読み取る必要があります。
現在、私はRandomAccessFileからバイトバッファに読み込み、必要な変数をビューバッファから抽出しています。これはうまくいきますが、非常に大きなファイルの場合は非常に遅いです。
ファイルを5000トレースMappedByteBuffersに分割したマッピングされたバイトバッファを使用して、新しいファイルハンドラを作成しました。これはうまく動作し、システムのメモリが不足してクロールが遅くなり、Macをもう一度使用可能にするために再起動する必要があるまで、非常に高速です。
私のプログラムが終了しても何らかの理由でバッファからのメモリが解放されることはありません。パージを行うか再起動する必要があります。
これは私のコードです。どんな提案も大歓迎です。
package MyFileHandler;
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
public class MyFileHandler
{
/*
A buffered file IO class that keeps NTRACES traces in memory for reading and writing.
the buffers start and end at trace boundaries and the buffers are sequential
i.e 1-20000,20001-40000, etc
The last, or perhaps only buffer will contain less than NTRACES up to the last trace
The arrays BufferOffsets and BufferLengths contain the start and length for all the
buffers required to read and write to the file
*/
private static int NTRACES = 5000;
private boolean HighByte;
private long FileSize;
private int BytesPerTrace;
private FileChannel FileChnl;
private MappedByteBuffer Buffer;
private long BufferOffset;
private int BufferLength;
private long[] BufferOffsets;
private int[] BufferLengths;
private RandomAccessFile Raf;
private int BufferIndex;
private ArrayList Maps;
public MyFileHandler(RandomAccessFile raf, int bpt)
{
try
{
HighByte = true;
// allocate a filechannel to the file
FileChnl = raf.getChannel();
FileSize = FileChnl.size();
BytesPerTrace = bpt;
SetUpBuffers();
BufferIndex = 0;
GetNewBuffer(0);
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
private void SetUpBuffers()
{
// get number of traces in entire file
int ntr = (int) ((FileSize - 3600)/BytesPerTrace);
int nbuffs = ntr/NTRACES;
// add one to nbuffs unless filesize is multiple of NTRACES
if (Math.IEEEremainder(ntr, NTRACES) != 0)
{
nbuffs++;
}
BufferOffsets = new long[nbuffs];
BufferLengths = new int[nbuffs];
// BuffOffset are in bytes, not trace numbers
//get the offsets and lengths of each buffer
for (int i = 0; i < nbuffs; i++)
{
if (i == 0)
{
// first buffer contains EBCDIC header 3200 bytes and binary header 400 bytes
BufferOffsets[i] = 0;
BufferLengths[i] = 3600 + (Math.min(ntr, NTRACES) * BytesPerTrace);
} else
{
BufferOffsets[i] = BufferOffsets[i - 1] + BufferLengths[i - 1];
BufferLengths[i] = (int) (Math.min(FileSize - BufferOffsets[i], NTRACES * BytesPerTrace));
}
}
GetMaps();
}
private void GetMaps()
{
// map the file to list of MappedByteBuffer
Maps = new ArrayList(BufferOffsets.length);
try
{
for(int i=0;i<BufferOffsets.length;i++)
{
MappedByteBuffer map = FileChnl.map(FileChannel.MapMode.READ_WRITE, BufferOffsets[i], BufferLengths[i]);
SetByteOrder(map);
Maps.add(map);
}
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
private void GetNewBuffer(long offset)
{
if (Buffer == null || offset < BufferOffset || offset >= BufferOffset + BufferLength)
{
BufferIndex = GetBufferIndex(offset);
BufferOffset = BufferOffsets[BufferIndex];
BufferLength = BufferLengths[BufferIndex];
Buffer = (MappedByteBuffer)Maps.get(BufferIndex);
}
}
private int GetBufferIndex(long offset)
{
int indx = 0;
for (int i = 0; i < BufferOffsets.length; i++)
{
if (offset >= BufferOffsets[i] && offset < BufferOffsets[i]+BufferLengths[i])
{
indx = i;
break;
}
}
return indx;
}
private void SetByteOrder(MappedByteBuffer ByteBuff)
{
if (HighByte)
{
ByteBuff.order(ByteOrder.BIG_ENDIAN);
} else
{
ByteBuff.order(ByteOrder.LITTLE_ENDIAN);
}
}
// public methods to read, (get) or write (put) an array of types, byte, short, int, or float.
// for sake of brevity only showing get and put for ints
public void Get(int[] buff, long offset)
{
GetNewBuffer(offset);
Buffer.position((int) (offset - BufferOffset));
Buffer.asIntBuffer().get(buff);
}
public void Put(int[] buff, long offset)
{
GetNewBuffer(offset);
Buffer.position((int) (offset - BufferOffset));
Buffer.asIntBuffer().put(buff);
}
public void HighByteOrder(boolean hb)
{
// all byte swapping is done by the buffer class
// set all allocated buffers to same byte order
HighByte = hb;
}
public int GetBuffSize()
{
return BufferLength;
}
public void Close()
{
try
{
FileChnl.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
}
Closeメソッドを呼び出す前にreturn文を実行している可能性がありますか? – Orin
マップされたバッファのリリースについての背景は、次のとおりです。http://bugs.java.com/view_bug.do?bug_id=4724038 – erickson