2016-05-20 16 views
0

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(); 
     } 
    } 
} 
+0

Closeメソッドを呼び出す前にreturn文を実行している可能性がありますか? – Orin

+1

マップされたバッファのリリースについての背景は、次のとおりです。http://bugs.java.com/view_bug.do?bug_id=4724038 – erickson

答えて

0

あなたはMappedByteBuffersの可能性が多数を経由して、ファイル全体をメモリにマッピングしている、とあなたは彼らがリリースされることはありませんMapでそれらを維持しているよう。それは無意味です。ファイル全体をMappedByteBufferという単一のアドレス、またはアドレスの制限を解決するために必要な最小の番号でマップすることもできます。あなたが必要とする以上に多くのものを使うことには利点はありません。

しかし、私はのファイルのセグメントを現在表示/編集中のにマップして、ユーザーが別のセグメントに移動したときにそれをリリースします。

私は、MappedByteBufferがはるかに高速であることがわかりました。私がテストした最後の時間に、マップされたバイトバッファを介した読み込みは、RandomAccessFileよりわずか20%速く、全く書き込まれませんでした。私はRandomAccessFileコードを見たいと思っていますが、おそらく簡単に修正できるかもしれない問題があるようです。

+0

AndroidでMappedByteBuffersをリリースする方法を知っていますか? – Karussell

関連する問題