HashMap に 50,000 個のオブジェクトを挿入すると OutOfMemoryError が発生するのはなぜですか?

StackOverflow https://stackoverflow.com/questions/235047

  •  04-07-2019
  •  | 
  •  

質問

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

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:508)
    at java.util.HashMap.addEntry(HashMap.java:799)
    at java.util.HashMap.put(HashMap.java:431)
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

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

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

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

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

役に立ちましたか?

解決

-Xmx128m(128はメガバイト数)をjavaに渡すことで、最大ヒープサイズを増やすことができます。デフォルトのサイズは思い出せませんが、かなり小さいものであることがわかりました。

ランタイムクラス。

// 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 Tuningページで。

>

他のヒント

HashMapのパラメーターを変更してメモリ要件を厳しくすることを提案している人もいます。 推測する代わりに測定することをお勧めします。 OOMEを引き起こす他の何かかもしれません。特に、 NetBeans Profiler または VisualVM (Java 6に付属していますが、Java 5で動けないことがわかります)。

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

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

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

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

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

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

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

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

追加して編集: オブジェクトがプロファイラーで実際に使用しているメモリ量を把握したら、弱参照またはソフト参照を調べて、不要なときにガベージコレクターからのメモリの人質の一部を誤って保持しないようにすることができます。長く使用します。

これもご覧ください:

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

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

デフォルトでは、JVMは限られたヒープスペースを使用します。制限はJVM実装に依存しており、使用しているJVMが明確ではありません。 Windows以外のOSでは、2 Gb以上のマシン上の32ビットSun JVMは、物理メモリの1/4のデフォルトの最大ヒープサイズ、つまり512 Mbを使用します。ただし、<!> quot; client <!> quot;のデフォルトモードJVMは最大64 MBの最大ヒープサイズです。他のベンダーのJVMは異なるデフォルトを選択する場合があります。

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

大まかな推測として、ハッシュテーブルは約16 MBしか使用しないため、ヒープ上に他の大きなオブジェクトがいくつか存在する必要があります。 ComparableTreeMapキーを使用できる場合、メモリを節約できます。

<!> quot; 5.0 JVMの人間工学<!> quot; で詳細を確認してください。

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

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

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top