2

ディスクベースのファイルと処理するMappedByteBuffersのページングリストを使用している非常に大きなdoubleの配列があります。詳細については、この質問を参照してください。Java1.5を使用してWindowsXPで実行しています。

これが、ファイルに対してバッファの割り当てを行う私のコードの重要な部分です...

try 
{
 // create a random access file and size it so it can hold all our data = the extent x the size of a double
 f = new File(_base_filename);
 _filename = f.getAbsolutePath();
 _ioFile = new RandomAccessFile(f, "rw");
 _ioFile.setLength(_extent * BLOCK_SIZE);
    _ioChannel = _ioFile.getChannel();

    // make enough MappedByteBuffers to handle the whole lot
 _pagesize = bytes_extent;
 long pages = 1;
 long diff = 0;
 while (_pagesize > MAX_PAGE_SIZE)
 {
  _pagesize  /= PAGE_DIVISION;
  pages *= PAGE_DIVISION;

  // make sure we are at double boundaries.  We cannot have a double spanning pages
  diff = _pagesize  % BLOCK_SIZE;
  if (diff != 0) _pagesize  -= diff;

 }

 // what is the difference between the total bytes associated with all the pages and the
 // total overall bytes?  There is a good chance we'll have a few left over because of the
 // rounding down that happens when the page size is halved
 diff = bytes_extent - (_pagesize  * pages);
 if (diff > 0)
 {
  // check whether adding on the remainder to the last page will tip it over the max size
  // if not then we just need to allocate the remainder to the final page
  if (_pagesize  + diff > MAX_PAGE_SIZE)
  {
   // need one more page
   pages++;
  }
 }

 // make the byte buffers and put them on the list
 int size = (int) _pagesize ;  // safe cast because of the loop which drops maxsize below Integer.MAX_INT
 int offset = 0;
 for (int page = 0; page < pages; page++)
 {
  offset = (int) (page * _pagesize );

  // the last page should be just big enough to accommodate any left over odd bytes
  if ((bytes_extent - offset) < _pagesize )
  {
   size = (int) (bytes_extent - offset);
  }

  // map the buffer to the right place 
     MappedByteBuffer buf = _ioChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);

     // stick the buffer on the list
     _bufs.add(buf);
 }

 Controller.g_Logger.info("Created memory map file :" + _filename);
 Controller.g_Logger.info("Using " + _bufs.size() + " MappedByteBuffers");
    _ioChannel.close();
    _ioFile.close(); 
} 
catch (Exception e) 
{
 Controller.g_Logger.error("Error opening memory map file: " + _base_filename);
 Controller.g_Logger.error("Error creating memory map file: " + e.getMessage());
 e.printStackTrace();
 Clear();
    if (_ioChannel != null) _ioChannel.close();
    if (_ioFile != null) _ioFile.close();
 if (f != null) f.delete();
 throw e;
} 

2番目または3番目のバッファーを割り当てた後、タイトルに記載されているエラーが発生します。

使用可能な連続メモリと関係があると思ったので、さまざまなサイズとページ数で試してみましたが、全体的なメリットはありませんでした。

「このコマンドを処理するのに十分なストレージが利用できない」とは正確にはどういう意味で、もしあれば、私はそれについて何ができますか?

MappedByteBuffersのポイントは、ヒープに収まるよりも大きな構造体を処理し、それらをメモリ内にあるかのように処理できることだと思いました。

手がかりはありますか?

編集:

以下の回答(@adsk)に応じて、コードを変更したので、一度に複数のアクティブなMappedByteBufferが存在することはありません。現在マップされていないファイルの領域を参照するときは、既存のマップをジャンクして新しいマップを作成します。約3回のマップ操作の後でも、同じエラーが発生します。

GCがMappedByteBuffersを収集しないことで引用されたバグは、JDK1.5でもまだ問題のようです。

4

2 に答える 2

3

MappedByteBuffersのポイントは、ヒープに収まるよりも大きな構造体を処理し、それらをメモリ内にあるかのように処理できることだと思いました。

いいえ。アイデアは、十分なメモリがあり、64ビットJVMを使用していることを前提として、2**31倍以上にアドレス指定できるようにすることでした。

(これは、この質問のフォローアップ質問であると想定しています。)

編集:明らかに、より多くの説明が必要です。

関係する制限はいくつかあります。

  1. lengthJavaには、配列の属性と配列インデックスの型がであるという基本的な制限がありますint。これは、署名されており、配列が負のサイズを持つことができないという事実と組み合わされて、int可能な最大の配列が2**31要素を持つことができることを意味します。この制限は、32ビットおよび64ビットのJVMに適用されます。これはJava言語の基本的な部分です...char値がからに移行するという事実のよう065535

  2. 2**3232ビットJVMを使用すると、JVMがアドレス指定できるバイト数に(理論上の)上限が設定されます。これには、ヒープ全体、使用するコード、ライブラリクラス、JVMのネイティブコードコア、マップされたバッファに使用されるメモリなどが含まれます。(実際、プラットフォームによっては、アドレス空間の場合、OS2**32提供するバイト数よりもかなり少ない場合があります。)

  3. javaコマンドラインで指定するパラメータは、JVMがアプリケーションで使用できるヒープメモリの量を決定します。オブジェクトの使用にマップされたメモリMappedByteBufferは、これにはカウントされません。

  4. OSが提供するメモリの量は、構成されているスワップスペースの合計量、「プロセス」の制限などによって異なります(Linux / UNIXの場合)。同様の制限がおそらくWindowsにも当てはまります。そしてもちろん、ホストOSが64ビット対応であり、64ビット対応のハードウェアを使用している場合にのみ64ビットJVMを実行できます。(Pentiumをお持ちの場合は、運が悪いのは明らかです。)

  5. 最後に、システム内の物理メモリの量が関係します。理論的には、マシンの物理メモリよりも何倍も大きいヒープなどを使用するようにJVMに要求できます。実際には、これは悪い考えです。仮想メモリを過剰に割り当てると、システムがクラッシュし、アプリケーションのパフォーマンスが低下します。

持ち帰りはこれです:

  • 32ビットJVMを使用する場合、おそらくアドレス可能なメモリのバイト2**31とバイトの間のどこかに制限されます。これは、配列を使用するかマップされたバッファーを使用するかに関係なく、最大値が2倍になる2**32のに十分なスペースです。2**292**30

  • 64ビットJVMを使用する場合は、2**31doubleの単一配列を表すことができます。マップされたバッファの理論上の制限は2**63バイトまたは2**612倍になりますが、実際の制限はおおよそマシンにある物理メモリの量になります。

于 2009-12-18T09:08:27.203 に答える
1

ファイルをメモリ マッピングすると、32 ビット VM でアドレス空間が不足する可能性があります。これは、ファイルが小さなチャンクでマップされ、それらの ByteBuffers に到達できなくなった場合でも発生します。その理由は、バッファを解放するために GC が開始されないためです。

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205でバグを参照してください。

于 2009-12-18T09:12:55.557 に答える