14

約 50,000 個のオブジェクト (したがって 50,000 個のキー) を に挿入しようとしていjava.util.HashMap<java.awt.Point, Segment>ます。ただし、OutOfMemory 例外が発生し続けます。(Segment私自身のクラス - 非常に軽量 - 1 つのStringフィールドと 3 つintのフィールド)。

スレッド「メイン」の例外 java.lang.OutOfMemoryError: Java ヒープ領域
    java.util.HashMap.resize (HashMap.java:508) で
    java.util.HashMap.addEntry (HashMap.java:799) で
    java.util.HashMap.put(HashMap.java:431)で
    bus.tools.UpdateMap.putSegment(UpdateMap.java:168) で

仮想メモリ用の空き RAM と HD スペースの両方で、マシン上で利用可能なメモリが十分にあることがわかっているので、これは非常にばかげているように思えます。

Javaがいくつかの厳しいメモリ要件で実行されている可能性はありますか? これらを増やすことはできますか?

に奇妙な制限がありHashMapますか?自分で実装する必要がありますか?他に注目すべきクラスはありますか?

(2GB RAM の Intel マシンで OS X 10.5 の下で Java 5 を実行しています。)

4

10 に答える 10

22

-Xmx128m (128 はメガバイト数) を Java に渡すことで、最大ヒープ サイズを増やすことができます。デフォルトのサイズは覚えていませんが、かなり小さかったように思います。

Runtimeクラスを使用して、使用可能なメモリ量をプログラムで確認できます。

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

( Java Developers Almanacの例)

これは、Java HotSpot VM に関するよくある質問Java 6 GC チューニング ページでも部分的に取り上げられています。

于 2008-10-24T19:55:32.717 に答える
7

HashMap のパラメータを変更してメモリ要件を厳しくすることを提案する人もいます。推測するのではなく、測定することをお勧めします。OOME を引き起こしている別の原因である可能性があります。特に、NetBeans ProfilerまたはVisualVM (Java 6 に付属していますが、Java 5 で行き詰まっているようです) のいずれかを使用することをお勧めします。

于 2008-10-24T20:32:09.163 に答える
4

オブジェクトの数が事前にわかっている場合に試すもう 1 つの方法は、デフォルトの (16,0.75) を使用するデフォルトの引数なしコンストラクターの代わりに HashMap(int capacity,double loadfactor) コンストラクターを使用することです。HashMap の要素数が (capacity * loadfactor) を超えた場合、HashMap の基になる配列は次の 2 の累乗にサイズ変更され、テーブルは再ハッシュされます。この配列には、連続したメモリ領域も必要です。たとえば、32768 サイズの配列を 65536 サイズの配列に 2 倍にする場合は、256kB の空きメモリのチャンクが必要になります。余分な割り当てと再ハッシュのペナルティを回避するには、最初からより大きなハッシュ テーブルを使用します。また、マップに収まる十分な大きさの連続したメモリ領域がなくなる可能性も減少します。

于 2008-10-24T20:15:57.667 に答える
3

通常、実装は配列によってサポートされます。配列は、固定サイズのメモリ ブロックです。ハッシュマップの実装は、これらの配列の 1 つに特定の容量 (たとえば 100 オブジェクト) でデータを格納することから始まります。

配列がいっぱいになり、オブジェクトを追加し続けると、マップは密かに配列サイズを増やす必要があります。配列は固定されているため、メモリ内に、現在の配列と共に、わずかに大きいまったく新しい配列を作成することによってこれを行います。これは、アレイの拡張と呼ばれます。次に、古い配列のすべての項目が新しい配列にコピーされ、古い配列は、ガベージ コレクションが行われ、ある時点でメモリが解放されることを期待して逆参照されます。

通常、アイテムをより大きな配列にコピーしてマップの容量を増やすコードが、このような問題の原因です。古い配列のサイズに基づいて新しい配列のサイズを決定する成長または負荷係数を使用する「愚かな」実装とスマートな実装があります。これらのパラメーターを非表示にする実装と非表示にする実装があるため、常に設定できるとは限りません。問題は、設定できない場合、2 などのデフォルトの負荷係数が選択されることです。そのため、新しい配列は古い配列の 2 倍のサイズになります。これで、おそらく 50k のマップに 100k のバッキング配列ができました。

負荷率を 0.25 程度まで下げることができるかどうかを確認してください。これにより、ハッシュ マップの衝突がさらに発生し、パフォーマンスが低下しますが、メモリのボトルネックが発生しているため、そうする必要があります。

次のコンストラクタを使用します。

( http://java.sun.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int , float))

于 2008-10-24T20:16:06.370 に答える
2

Java を起動するときに、フラグ -Xmx512m またはそれ以上の数値を設定する必要がある可能性があります。64MBがデフォルトだと思います。

追加するために編集: プロファイラーでオブジェクトが実際に使用しているメモリ量を把握した後、弱参照またはソフト参照を調べて、ガベージ コレクターから誤ってメモリの人質を保持していないことを確認する必要がある場合があります。もう使用していません。

于 2008-10-24T20:04:05.553 に答える
1

また、これを見てみたいかもしれません:

http://java.sun.com/docs/hotspot/gc/

于 2008-10-24T19:57:56.597 に答える
1

ランダムな考え: HashMap に関連付けられたハッシュ バケットは、メモリ効率が特に優れているわけではありません。別の方法として TreeMap を試してみて、それでも十分なパフォーマンスが得られるかどうかを確認してください。

于 2008-10-26T03:47:34.983 に答える
1

これらの回答では、Java のメモリ サイズは固定されており、構成された最大ヒープ サイズを超えて成長しないことが暗示されています。これは、たとえば、実行されているマシンによってのみ制約される C とは異なります。

于 2008-10-24T20:09:28.740 に答える
1

デフォルトでは、JVM は限られたヒープ スペースを使用します。制限は JVM の実装に依存しており、どの JVM を使用しているかは明確ではありません。Windows 以外の OS では、2 Gb 以上のマシン上の 32 ビット Sun JVM は、物理メモリの 1/4、またはこの場合は 512 Mb のデフォルトの最大ヒープ サイズを使用します。ただし、「クライアント」モードの JVM のデフォルトは、最大ヒープ サイズが 64 Mb にすぎないため、これに遭遇した可能性があります。他のベンダーの JVM は異なるデフォルトを選択する場合があります。

もちろん、-Xmx<NN>mオプション to を使用してヒープ制限を明示的に指定できますjava。ここ<NN>で、 はヒープのメガバイト数です。

大まかな推測として、ハッシュ テーブルは約 16 Mb しか使用しないはずなので、ヒープには他の大きなオブジェクトがいくつかあるはずです。Comparableでキーを使用できればTreeMap、メモリを節約できます。

詳細については、「5.0 JVM のエルゴノミクス」を参照してください。

于 2008-10-24T20:14:16.687 に答える
1

Java ヒープ スペースはデフォルトで制限されていますが、それでも極端に聞こえます (ただし、50000 セグメントの大きさはどれくらいですか?)

すべてが同じ「スロット」に割り当てられるため、セット内の配列が大きくなりすぎるなど、他の問題があると思われます(もちろん、パフォーマンスにも影響します)。ただし、ポイントが均一に分布している場合は、そうは思えません。

TreeMap ではなく HashMap を使用しているのはなぜですか? ポイントは 2 次元ですが、compare 関数を使用してサブクラス化し、log(n) ルックアップを実行できます。

于 2008-10-24T20:23:48.947 に答える