ホームページ  >  記事  >  Java  >  Java のメモリ割り当てとリサイクル戦略に関する簡単な説明

Java のメモリ割り当てとリサイクル戦略に関する簡単な説明

巴扎黑
巴扎黑オリジナル
2017-06-26 10:41:411069ブラウズ

1. はじめに

Java テクノロジー システムで言及されている自動メモリ管理は、最終的には 2 つの問題です: メモリ割り当てとリサイクルです。今日は、Java リサイクルの存在についてお話しましょう。メモリ内の Java オブジェクトの割り当て。平たく言えば、オブジェクトのメモリ割り当てはヒープ上の割り当てです。オブジェクトは主に新しい世代の Eden に割り当てられます (メモリ内のオブジェクトの生成はガベージ コレクション中に補充されます)。詳しく知りたい場合は、次のことを確認してください。 「Java 仮想マシンの詳細な理解」も参照してください)。ローカル スレッド割り当てバッファが開始されると、スレッドの優先順位に従って TLAB 上に割り当てられます。場合によっては、古い世代に直接割り当てられることもあります。

2. 古典的な割り当て戦略

1. オブジェクトは最初に Eden に割り当てられます

一般に、Eden に割り当てのための十分なスペースがない場合、jvm はマイナー GC を開始します。それでも十分なスペース割り当てがない場合は、後で説明する他の対策もあります。

仮想マシンの奇数ログパラメータ -XX:+PrintGCDetails を設定すると、ガベージコレクション中にメモリリサイクルログが出力され、プロセス終了時に各メモリ領域の現在の割り当てが出力されます。具体的な例を見てみましょう。まず、jvm パラメータ -Xms20m -Xmx20m -Xmn10m を設定する必要があります。これらの 3 つのパラメータは、Java ヒープ サイズが 20M で、残りの 10M は拡張できないことを示します。古い世代に割り当てられます。 -XX:SurvivorRatio=8 は、新世代の jvm における Eden と Survivor のデフォルトの比率です。デフォルトは 8:1 です。その理由は、新しい世代のオブジェクトの 98% が次の GC でリサイクルされるため、ガベージ コレクションにコピー アルゴリズムを使用するのが非常に適しているためです。そのため、新しい世代の 10M メモリのうち 8M が Eden になります。 1M は Survivor で、残りの 1M は未使用です。コピー アルゴリズムと連携するメモリ ブロックも Survivor です。

 1 public class ReflectTest { 2  3     private static final int _1MB = 1024*1024; 4      5     public static void testAllocation(){ 6         byte[] allocation1 , allocation2 , allocation3 , allocation4; 7         allocation1 = new byte[2 * _1MB]; 8         allocation2 = new byte[2 * _1MB]; 9         allocation3 = new byte[2 * _1MB];10         allocation4 = new byte[6 * _1MB];11     }12     13     public static void main(String[] args) {14         ReflectTest.testAllocation();15     }16     17 }

出力は次のとおりです

Heap
 PSYoungGen      total 9216K, used 6651K [0x000000000b520000, 0x000000000bf20000, 0x000000000bf20000)
  eden space 8192K, 81% used [0x000000000b520000,0x000000000bb9ef28,0x000000000bd20000)
  from space 1024K, 0% used [0x000000000be20000,0x000000000be20000,0x000000000bf20000)
  to   space 1024K, 0% used [0x000000000bd20000,0x000000000bd20000,0x000000000be20000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab20000, 0x000000000b520000, 0x000000000b520000)
  object space 10240K, 60% used [0x000000000ab20000,0x000000000b120018,0x000000000b520000)
 PSPermGen       total 21248K, used 2973K [0x0000000005720000, 0x0000000006be0000, 0x000000000ab20000)
  object space 21248K, 13% used [0x0000000005720000,0x0000000005a07498,0x0000000006be0000)

eden が 81% を占めていることがわかり、allocation1、allocation2、allocation3 がすべて新世代 Eden に割り当てられていることを示します。

2. ラージ オブジェクトは古い世代で直接割り当てられます。

ラージ オブジェクトとは、非常に長い文字列や配列と同様に、格納するために大量の連続メモリ領域を必要とするオブジェクトを指します。大きなオブジェクトは仮想マシンのメモリ分散にとって好ましくありません。1 ラウンドしか存続しない大きなオブジェクトが多数発生すると、コードを作成するときにそのような問題を回避する必要があります。 -XX:PretenureSizeThreshold パラメータは、この値より大きいオブジェクトが古い世代に直接割り当てられます。これは、Eden 領域と Survivor 領域の間で大量のメモリがコピーされることを避けるためです。前に述べた リサイクルアルゴリズムとコピーアルゴリズムについては前に述べたので、詳細は説明しません。

public class ReflectTestBig {private static final int _1MB = 1024*1024;    public static void testAllocation(){byte[] allocation2 , allocation3 , allocation4;allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[6 * _1MB];
    }    public static void main(String[] args) {
        ReflectTestBig.testAllocation();
    }
    
}

出力は以下の通りです

Heap
 PSYoungGen      total 8960K, used 4597K [0x000000000b510000, 0x000000000bf10000, 0x000000000bf10000)
  eden space 7680K, 59% used [0x000000000b510000,0x000000000b98d458,0x000000000bc90000)
  from space 1280K, 0% used [0x000000000bdd0000,0x000000000bdd0000,0x000000000bf10000)
  to   space 1280K, 0% used [0x000000000bc90000,0x000000000bc90000,0x000000000bdd0000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab10000, 0x000000000b510000, 0x000000000b510000)
  object space 10240K, 60% used [0x000000000ab10000,0x000000000b110018,0x000000000b510000)
 PSPermGen       total 21248K, used 2973K [0x0000000005710000, 0x0000000006bd0000, 0x000000000ab10000)
  object space 21248K, 13% used [0x0000000005710000,0x00000000059f7460,0x0000000006bd0000)

割り当て4が設定した-XX:PretenureSizeThreshold=3145728を超えており、割り当て4が旧世代に直接割り当てられ、旧世代の占有率が60%になっていることがわかります。 。設定 -XX:PretenureSizeThreshold=3145728 を -XX:PretenureSizeThreshold=3m として記述することはできないことに注意してください。そうしないと、jvm がそれを認識しません。

3. 長期的に存続するオブジェクトは古い世代に入ります

仮想マシンはバンドコレクションの考え方を採用してメモリを管理するため、メモリのリサイクルではどのオブジェクトを新しい世代に配置する必要があるかを特定する必要があります。旧世代に置かれます。この目的を達成するために、jvm はオブジェクトごとに年齢カウンター (Age) を定義します。オブジェクトが Eden で生まれ、最初のマイナー GC を生き延び、Survivor に保存できる場合、そのオブジェクトは Survivor に移動され、オブジェクトの年齢は 1 に設定されます。オブジェクトがマイナー GC をエスケープするたびに、その年齢は 1 ずつ増加します。オブジェクトの年齢がしきい値の 1 年を超えると、オブジェクトは古い世代に昇格します。このしきい値 jvm のデフォルトは 15 で、-XX:MaxTenuringThreshold で設定できます。

   m = 1024 * 1024  [] a1 =  [1 * m / 4[] a2 =  [7 *[] a3 =  [3 * m];

出力は以下の通りです

[GC [DefNew: 7767K->403K(9216K), 0.0062209 secs] 7767K->7571K(19456K), 0.0062482 secs]   
[Times: user=0.00 sys=0.00, real=0.01 secs]   
a3 ok  
Heap  
 def new generation   total 9216K, used 3639K [0x331d0000, 0x33bd0000, 0x33bd0000)  
  eden space 8192K,  39% used [0x331d0000, 0x334f9040, 0x339d0000)  
  from space 1024K,  39% used [0x33ad0000, 0x33b34de8, 0x33bd0000)  
  to   space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)  
 tenured generation   total 10240K, used 7168K [0x33bd0000, 0x345d0000, 0x345d0000)  
   the space 10240K,  70% used [0x33bd0000, 0x342d0010, 0x342d0200, 0x345d0000)  
 compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)  
   the space 12288K,   3% used [0x345d0000, 0x3462f548, 0x3462f600, 0x351d0000)  
    ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)  
    rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)

a2 は一度生き残っており、その年齢は 1 で、設定 -XX:MaxTenuringThreshold=1 を満たしているため、a2 は古い世代に入り、a3 は新しい世代に入っていることがわかります。

4. 動的なオブジェクトの経過時間の決定

さまざまなプログラムのメモリ状態に適切に適応するために、仮想マシンはオブジェクトの経過時間が -XX:MaxTenuringThreshold で設定された値に達する必要があるとは限りません。 Survivor スペース内の同じ年齢のすべてのオブジェクトのサイズの合計が Survivor スペースの半分より大きい場合、古い世代に昇格する必要はありません。 -XX:MaxTenuringThreshold で設定された値に達しました。

5. スペース割り当ての保証

マイナー GC が発生すると、仮想マシンは古い世代への各昇格の平均サイズが古い世代の残りのスペースより大きいかどうかを検出します。直接実行されます。未満の場合は、HandlerPromotionFailyre 設定で保証失敗が許可されているかどうかを確認します。許可されている場合は、マイナー GC のみが実行されます。許可されていない場合は、FUll GC も改善されます。つまり、新世代 Eden が変更されたオブジェクトを保存できない場合、オブジェクトは旧世代に保存されます。

3. 一般的に使用される JVM パラメータ設定

1. -Xms: 初期ヒープ サイズ、デフォルト (MinHeapFreeRatio パラメータは調整可能) 空きヒープ メモリが 40% 未満の場合、JVM は最大制限までヒープを増加します。 -XMX。

2.

3. -Xmn: 若い世代のサイズ (1.4 または lator)、ここでのサイズは (eden+2 survivor space) であり、jmap -heap で示される新しい世代とは異なります。
ヒープ全体のサイズ = 若い世代のサイズ + 古い世代のサイズ + 永続的な世代のサイズ。
若い世代を増やした後は、古い世代のサイズが減少します。この値は、ヒープ全体の 3/8 の構成をシステムのパフォーマンスに大きく影響します。

4. -XX:NewSize: 若い世代のサイズを設定します (1.3/1.4 の場合)。

5. -XX:MaxNewSize: 若い世代の最大値 (1.3/1.4 の場合)。

6. -XX:PermSize: 永続世代 (perm gen) の初期値を設定します。

7. -XX:MaxPermSize: 永続世代の最大サイズを設定します。

8、-Xss: 各スレッドのスタック サイズ。JDK5.0 以降、各スレッドのスタック サイズは 1M でした。アプリケーション スレッドが必要とするメモリ サイズは 256K でした。同じ物理アンダーメモリでは、この値を減らすとより多くのスレッドを生成できますが、オペレーティング システムにはプロセス内のスレッド数に制限があり、経験値は約 3000 ~ です。 5000。

9、-XX:NewRatio: 古い世代 (永続世代を除く) に対する若い世代 (エデンと 2 つの Survivor エリアを含む) の比率、-XX:NewRatio=4 は、若い世代と古い世代の比率を意味します。古い世代は 1:4、若い世代はスタック全体の 1/5 を占めます。 Xms=XmxかつXmnを設定した場合、本パラメータの設定は不要です。

10、-XX:SurvivorRatio: Eden エリアと Survivor エリアのサイズ比を 8 に設定すると、2 つの Survivor エリアと 1 つの Eden エリアの比率は 2:8 となり、1 つの Survivor エリアは 1/10 を占めます。若い世代全体の。

11. -XX:LargePageSizeInBytes: Perm のサイズに影響を与えるため、メモリ ページのサイズをあまり大きく設定することはできません。

12、-XX:+DisableExplicitGC: System.gc() をオフにします

13、-XX:MaxTenuringThreshold: ガベージの最大存続期間を 0 に設定すると、若い世代のオブジェクトは Survivor 領域を経由せずに直接実行されます。古い世代を多数含むアプリケーションの場合、この値をより大きな値に設定すると、若い世代のオブジェクトが Survivor 領域に複数回コピーされるため、生存時間が長くなる可能性があります。若い世代のオブジェクトの数を増やし、若い世代がリサイクルされる確率。このパラメータはシリアル GC でのみ有効です。

14、-XX:PretenureSizeThreshold: サイズを超える場合、オブジェクトは古い世代に直接割り当てられます。新しい世代が Parallel Scavenge GC を使用する場合、ユニット バイトは無効になります。大きな配列オブジェクト、およびその配列には外部参照オブジェクトがありません。

15、-XX:TLABWasteTargetPercent: eden エリア内の TLAB の割合。

4番目、補足

マイナーGCとフルGCの違い:

新世代GC(マイナーGC):ほとんどのJavaオブジェクトはGCの最初のラウンドを回避できないため、新世代で発生するガベージコレクションアクションを指します。 , そのため、マイナー GC は非常に頻繁に使用され、一般的に回復速度は比較的速いです。

古い世代の GC (FULL GC/Major GC): 古い世代で発生する GC を指します。Major GC が出現する場合、多くの場合、少なくとも 1 つのマイナー GC が伴います (ただし、絶対ではなく、直接 GC に含まれます)。 ParallelScavenge コレクターの収集戦略)、メジャー GC 選択プロセスを実行します。メジャー GC の速度は、通常、マイナー GC よりも 10 倍以上遅くなります。

以上がJava のメモリ割り当てとリサイクル戦略に関する簡単な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。