私の知る限り、ArrayList
を作成するとき:
ArrayList<String> list = new ArrayList<String>(SIZE);
JVMはそれを予約します メモリの連続部分 。リストに新しい要素を追加するとき、要素の数がSIZE
の75%に達すると、メモリの新しい連続部分を予約し、すべての要素をコピーします。
私たちのリストはどんどん大きくなっています。新しいオブジェクトを追加しているため、リストをもう一度再構築する必要があります。
今、何が起きた?
JVMはメモリの連続したセグメントを探していますが、十分なスペースが見つかりません。
ガベージコレクターは、未使用の参照をいくつか削除し、メモリを最適化することができます。このプロセスの後、JVMがリストの新しいインスタンス用のスペースを予約できない場合、どうなりますか?
可能な限り最大のセグメントを使用して、新しいセグメントを作成しますか?どのException
がスローされますか?
私はこの質問を読みました Java:ArrayListがメモリを管理する方法 そして答えの1つは:
参照は多くのスペースを消費しません。 しかし、とにかく、スペースの一部が使用されます。配列が大きくなると、問題になる可能性があります。また、メモリ空間を使用する別のものがあることを忘れることはできません。
JVMが要求された量のメモリを割り当てることができない場合、JVMはスローします
OutOfMemoryError
それでおしまい。実際、JVMのメモリ割り当てには次の2つの結果しかありません。
ある程度のメモリのような中間オプションはありません。
ArrayListとは関係ありません。これはJVMの問題です。 ArrayListが何らかの形でこの状況を特別な方法で管理しているかどうかを尋ねると、答えは「いいえ、そうではありません」です。必要なメモリ量を割り当てようとし、JVMに残りの部分を考慮させます。
Javaでは、オブジェクトへの参照は連続したメモリに格納されます。実際のオブジェクトは、連続していない状態を維持できます。したがって、たとえば、配列に10個のオブジェクトがある場合、JVMは、オブジェクトではなく、オブジェクト参照用にメモリを予約するだけで済みます。したがって、各参照がByte(正しい値ではない)を取得し、各オブジェクトがKBを取得し、10個の要素の配列がある場合、JVmは1 * 10 B(つまり10 B)の連続メモリを予約しようとします。オブジェクトは、合計10KBの10個の異なるメモリロケーションに存在できます。連続および非連続の両方のメモリ空間は、スレッドに割り当てられたメモリ用であることに注意してください。
配列のサイズを変更する必要がある場合、JVMは新しい長さの連続配列を見つけようとしました。したがって、配列を10要素から20要素にサイズ変更する場合は、20 KBの連続したスペースを予約しようとします(上記の例を使用)。このスペースを見つけると、古い配列から新しい配列への参照のコピーを作成します。このスペースが見つからない場合、GCを実行しようとします。それでもスペースが見つからない場合は、OutofMemoryExceptionがスローされます。
したがって、アレイのサイズを変更するときはいつでも、JVMは新しいサイズのアレイの参照を保存するために連続メモリを見つける必要があります。たとえば、配列を1000要素のサイズに拡張し、各参照がそれぞれeachである場合、JVmは1000 * 1KB(1 MB)の連続メモリを見つけようとします。このメモリが見つかった場合、参照のコピーを行い、GCが次に実行されるたびに古いコンティグオスメモリをGCにマークします。メモリが見つからない場合は、GCを実行しようとします。それでも連続メモリが見つからない場合、メモリ不足例外がスローされます
これは、サイズ変更を行うArrayListのコードです。 http://grepcode.com/file/repository.grepcode.com/Java/root/jdk/openjdk/6-b14/Java/util/ArrayList.Java#ArrayList.ensureCapacity%28int%29
これにより、新しい配列を割り当てるのに十分なヒープ領域がなくなるとすぐにOutOfMemoryError
がスローされます。
このエラーがスローされる前に、ガベージコレクションが常に行われます。これにより、メモリが圧縮され、使用されなくなった小さなサイズのアレイがすべて削除されます。しかし、古いコンテンツを新しいリストにコピーするには、古い配列、新しい配列、および含まれるすべてのオブジェクトを一度にすべてメモリに入れる必要があるという事実を回避する方法はありません。
したがって、メモリ制限が10 MBで、アレイが2 MBを占有し、サイズが最大3 MBで、文字列が6 MBを占有する場合、この操作の後は3 +しか使用できませんが、OOMがスローされます6 = 9 MBのメモリ。これを回避する1つの方法は、巨大な配列で本当にメモリ制限に近い値で実行したい場合、サイズを変更する必要がないように、配列を最初からフルサイズにすることです。
JVMが配列サイズを拡張できる場合に使用するスペースがないため、メモリが不足すると想定しています。
最初に修正したいのは、リストに新しい要素を追加するとき、要素数が100% SIZEに達すると、メモリの新しい連続部分を予約し、すべての要素をコピーすることです。要素。
ArrayListの新しいサイズは次のとおりです。
ArrayListのNewSize =(CurrentSize * 3/2)+ 1
しかし、この方法で行くことは決してお勧めできません。どれだけのオブジェクトを保存する必要があるかがわかっている場合、ArrayListの次のコンストラクタを使用できます。
ArrayList ar = new ArrayList(int initialCapacity);
JVMがArrayListのヒープに十分な連続スペースを指定できなかった場合、実行時に次のようになります。