マイクロチャンネル公衆数より転載:Javaの詳細アーキテクチャ(Javaの-jiagou)-----この記事を読んだ後、私の祖母は、JVMのメモリモデルとガベージコレクションを知っていました!
第六に、メモリモデル
6.1メモリモデルと実行時データ領域
それはさまざまな分野の数のデータを管理しますするJavaプログラムの実行中のJava仮想マシンのメモリ部門。
メモリに記憶された各変数の定義されたJavaメモリモデルプログラムのアクセスルール、即ち、そのような変数の基礎となる詳細の主な目的は、仮想マシンの変数にメモリから削除されます。
ここでは、メインメモリ、ワーキングメモリ、およびJava Javaヒープ、スタックのメモリ領域、及び他の方法ゾーンの話は基本的に無関係であり、どちらもメモリ分割と同じレベルではありません。彼らは関連付けを強制しなければならない場合は、変数、メインメモリから、ワーキングメモリ、メインメモリの定義を参照して、Javaヒープのデータ部分、および仮想マシンに対応するワーキングメモリに対応するメインオブジェクトインスタンスは、一部の領域でスタックです。
ランタイムデータ領域の顔をたくさん説明では、実際には、データ・ストレージ・ヒープおよびメソッド領域(非ヒープ)に焦点を当て、そのメモリは、この2つの領域(この2つの領域がスレッドで共有されていることに注意)からフォーカスを拡大するように設計されています。
仮想マシンのスタックの場合、ネイティブメソッドスタック、プログラムカウンタは、スレッドプライベートです。
6.2グラフィック
非ヒープ、ヒープ領域。
ヒープ領域は2つのブロックに分割され、1は旧地区は、地区は若いです。
若い2つのブロックに分割し、1つのサバイバー領域(S0 + S1)、領域はエデンです。エデン:S0:S1 = 8:1:1
S0とS1と同じ大きさ、またしてから呼び出すことができます。
導入前によるとヒープが、知っている汎用オブジェクトを作成し、配列はヒープメモリ空間に割り当てられますが、キーは非常に多くの分野、対象それ作成領域があるヒープするのですか?
6.3オブジェクトの作成領域
通常の状況下では、新しく作成されたオブジェクトがエデンの領域に割り当てられている、いくつかの特別なラージオブジェクトは、旧地区に直接割り当てられます。
例えば、A、B、Cなどがエデン領域に作成されたオブジェクトがありますが、エデンのメモリ空間は、確かな100M、100Mやかのように限られた領域は、設定されたしきい値に到達するために使用されてきたが、この時間は、あなたはエデンのメモリ空間に必要そのガベージコレクション(ごみの収集)、私たちはマイナーGC呼び出すGC、マイナーGC GCヤング領域を参照をクリーンアップします。
GC後、いくつかのオブジェクトが離れてクリアされ、一部のオブジェクトは生きてもよい、あなたはそのオブジェクトの生存のためのサバイバー領域にコピーして、これらのオブジェクトのエデンエリアを空にする必要があります。
6.4サバイバー詳細エリア
図から分かるように、サバイバーもとからと呼ばれる2つのS0とS1、に分けました。
同時点で、S0およびS1は、データ領域を有することができ、他の一つは空です。
上記のような開始領域、そこからのみエデンオブジェクトとしてGC、続いて、には空です。
GCの操作を行うために、地域内のオブジェクトから、年齢が+1されます、我々はすべてエデンエリアのライブオブジェクトはゾーンから、Toフィールドにコピーされている知っているこの時間は、2つのオブジェクトの場所があるでしょう生き残ることができます。
もし対象年齢設定年齢が閾値に達すると、オブジェクトは古い領域に移動される前に、オブジェクトのしきい値をToフィールドにコピーされ届きません。
エデンの面積と領域が空になったこの時から(GCは確かオブジェクト、GCの対象が自分の場所を持っていないではありません)。
この時からとからなるために前に、前になることから、役割を切り替えます。
それはどのような場合には生存者が空であることを確認するために呼ばれる領域です。
マイナーGCは、フィールドが満たされているために、すべてのオブジェクトは、古い時代にコピーされます知って、このプロセスを繰り返します。
6.5旧地区詳細
上記の分析から分かるように、老齢の一般的な面積が比較的大きいオブジェクトである、または相対がある閾値を超えたオブジェクト。
旧地区ではGCの操作旧主なGCとして知られている、我々GC面積を持つことになります、GCは1になります対象年齢のそれぞれの後に生き残ることができる、と年齢が一定のしきい値を超えた場合、それがリサイクルされます。
人生を理解する6.6オブジェクト
私は、私はエデンエリアで、エデンのエリアで生まれ、私は私の弟を見て、長いのような、我々はエデンエリアにかなり長い時間をプレイして、通常のJavaオブジェクトをしています。ある日、エデン面積があまりにも多くの人々ですが、私は、面積がサバイバーに行っているので、私は浮いて始まった、地域の「From」地区サバイバーに行くために、時には遺族エリアの「From」を余儀なくされました住所不定の「へ」の領域、時間のサバイバー。私は18だっないまで、私の父は、私が社会に別の試みのために行く、大人と述べました。
だから私は、古い世代、旧世代、多くの人々、そして年齢が大きいに来たあそこに行き、私はここで多くの人々を知っています。古い世代では、私は20年(各GCプラス一歳)のために住んでいたし、その後再利用しました。(この例では、理解することは非常に簡単ではありません?)
6.7よくある質問
メジャー/マイナー/フルGCを理解する方法
-
マイナーGC:新世代
-
主なGC:歳
-
フルGC:新生代+歳
なぜ我々は唯一のエデンはそれをしない?サバイバー領域が必要なのでしょうか?
-
一度各マイナーGCのためのサバイバー、エデン地区は、存在しない場合、オブジェクトは、古い時代の生存に送信されます。その結果、すぐに満たされるために、古い年は、メジャーGCをトリガーとして(メジャーGCは、一般的にマイナーGCを伴うので、また全GCをトリガとして見ることができます)。
-
メモリ空間は、古い時代の新世代よりもはるかに大きい、一度のフルGCは、マイナーGCよりもはるかに長く、消費しました。
-
害?フルGC時間の頻繁な消費が非常に長い長い時間の実装は、それが大規模なプログラムの実施や応答性に影響を与えます。あなたはそれが古いスペースを増加させることになるのですかわずかに少ない、と言うかもしれません。
-
古い宇宙年の増加、より多くのライブオブジェクトは、古い時代を埋めるためにした場合。しかし、古いのスペースが増加して、フルGCの頻度を削減しながら、フルGCが発生すると、実行時間が長くなるために必要。
-
フルGCが必要な時間を短縮するが、すぐに歳のライブオブジェクト、全GC周波数が増加を記入されますが、古いのスペースを削減する場合。
だから、サバイバーの感があり、オブジェクトを減らすことですがサバイバーを確保するために事前にスクリーニングすることにより、完全なGCの発生を低減、古い時代に送られた、唯一の目標16回マイナーGCを受け、新世代の中で生き残ることができ、それがに送信されます歳。
なぜ我々は2つのサバイバー領域が必要なのでしょうか?
最大の利点は、断片化を解消することです。サバイバー領域がない?最初の部分で、私たちは地区がサバイバーを設定する必要があります知っている理由です。我々は、同様のプロセスをシミュレートするかもしれませんが、一つだけサバイバー領域があることを今仮定します。
ただ、エデンがいっぱいになると、マイナーGCをトリガし、エデンで新しいオブジェクトを作成し、エデンでのライブオブジェクトは、サバイバーの領域に移動されます。これは、サイクルが続く続くハードサバイバーエリアのエデン領域に今回のライブオブジェクトは、それがあることは明らかである場合にはエデンがいっぱいになる次回は、問題は、この時点でマイナーGC、エデンとサバイバーは、ライブオブジェクトの数を持っています二つのメモリは、オブジェクトの一部が連続していない占められ、それはメモリの断片化につながります。
破片の自由な別の非空のサバイバースペースは、サバイバースペースが空で常にあります。
エデンの新世代:S1:1:1:なぜ、S2は8ですか?
空きメモリの新世代:1:9のメモリを保証するために使用されるアルゴリズムをコピー
利用可能なメモリエデン:1:S1領域が8であります
すなわち、エデンの新世代:S1:S2 = 8:1:1
P70の複製アルゴリズム
へ:1の比率近代商業の仮想マシンは、それが1を必要としない、「死に向けて生の夜」の約98%を、新生代を回復するために、このコレクションのアルゴリズムを使用してIBMのショーに特化した、オブジェクトの新しい世代されています大きなスペースと2つの小さなエデンSurvivor領域内のメモリ空間が、新しい世代のメモリを分割し、前記回収された場合、それぞれが、サバイバーエデンを使用し、エデンサバイバーはさらに生きているオブジェクト一度サバイバースペースの別の部分にコピーされ、最終的にはサバイバーエデンとちょうど使用スペースを一掃。1、すなわち、各新世代全体の容量(80%+ 10%)の90%の新世代のために利用可能なメモリ空間:ホットスポットVMエデンとサバイバーデフォルトのサイズ比は8です。したがって、追加のスペースが生じ回復し、対象の10%以上の生存率による新世代は、サバイバーライブオブジェクトを格納するのに十分なスペースがない場合。このとき、これらのオブジェクトは、保証メカニズムを割り当てることによって、古い年に直接移動します。
セブン、ごみは収集(ガベージコレクション)
彼は、GC、主要な旧地区GC、老いも若きもDistrict地区の全GCマイナーヤング地区として、ヒープメモリのガベージコレクションの前に言いました。しかし、オブジェクトに対して、どのようにそれがスパムであるかを決定するには?あなたはリサイクルする必要がありますか?どのようにそれを回復するには?したがって、これらの問題は、我々はまた、詳細に探求する必要があります。
Javaがあるので自動メモリ管理およびガベージコレクションが行うこと、およびガベージコレクションのすべての側面についての知識を理解していない場合、問題はいったんトラブルシューティングし、解決することは困難である、自動ガベージコレクションは、Javaヒープ内のオブジェクトを見つけて、オブジェクトを分類することですオブジェクトとオブジェクトが既に使用されている見つける、決定し、これらのオブジェクトを使用し、その後、使用されることはありませんが、ヒープから削除されません。
ランタイムデータ領域のすべての部門でP61ガベージコレクション
プログラムカウンタは、仮想マシンをスタック、ネイティブメソッドがオフスレッドと、三つの領域生まれスレッドとスタック;整然とスタックおよびスタック操作で行わ入り口と出口メソッドのスタックフレームとしてスタック。実質的に既知のクラスの構造が確定されたときに、いくつかの実行時にJITコンパイラによって最適化されるであろう(割り当てられているが、この章に基づくモデルで説明した概念は、一般的に考えることができるどのくらいのメモリ各スタックフレーム知り得るコンパイル)するので、メモリの割り当てとこれらの領域の回復はこれらの分野では、確実に装備されているだけで自然なメモリとして、メソッドの最後またはスレッドの終了以来、リサイクルの問題に多くの思考を与える必要はありません。回復しました。Javaヒープおよびメソッド領域は、クラスのより多くのメモリ・インタフェースを実装する必要があり、同じではありませんが、異なる場合があり、複数のブランチは、メモリの方法を同じである必要はないかもしれない、私たちは時に運転中に知るために唯一のプログラムでありますメモリの割り当てと回復の一部であるが作成されるオブジェクトは、動的なガベージコレクタの懸念は、このメモリです。
P68回復プロセスエリア
多くの人々は何のガベージコレクションが存在しないメソッド領域(永続的またはHotSpot仮想マシンの代わりに)と思い、Java仮想マシン仕様は、「仮想マシンの実装はガベージコレクション領域の方法では必要とされなくてもよいことを言うと、エリア内のごみ収集方法でしたスタックで、特に新しい世代に、ガベージコレクションルーチンアプリケーションは、宇宙の典型的には、70%〜95%を回収することができ、かつ恒久的な世代のガベージコレクションの効率はこれよりはるかに低いです:費用対効果「とは、一般的に低いです。
世代別ガベージコレクション二つの部分の主永久リサイクル:定数と役に立たない廃棄物のクラス。廃棄物とリサイクルのJavaヒープオブジェクトの一定のリサイクルは非常に似ています。定数リテラルプールを回復するには、例えば、文字列「ABC」は定数プールに入った場合は、しかし、現在のシステムは、任意の文字列オブジェクトは、「ABC」と呼ばれていない、つまり定数をStringオブジェクトの参照はありませんメモリのリサイクルがこの時点で発生し、必要に応じて、「ABC」、一定のシステムが一定のプール「招待」される場合は、「ABC」は定数プールは、これらのリテラルへの他の参照は、ありません。シンボル他のクラス(インターフェイス)、メソッド、定数プール参照のフィールドも同様です。
オブジェクトを識別するために、どのように7.1はゴミですか?
ガベージコレクションを実行するには、最初のオブジェクトがガベージ何であるかを知っている必要があります。
7.1.1参照カウント
オブジェクトの場合、単にアプリケーションで参照されたオブジェクトを保持し、そのオブジェクトがそれらへのポインタ参照を持っていない場合、それはゴミである、オブジェクトがガベージないことを意味します。
7.1.2到達可能性解析
GCルートオブジェクトによって、オブジェクトアップかどうかを確認するために先に探し始めます。
そのようなローカル変数メソッドスタックとしてクラスローダ、スレッドローカル変数テーブルの仮想マシンスタック、静的メンバ、一定の基準、:これは、GCルートとして使用することができます。
-
オブジェクト参照での仮想マシンのスタック(ローカル変数表のスタックフレーム)。
-
メソッド静的プロパティクラスオブジェクト参照エリア。
-
リテラルの参照方法ゾーンオブジェクト。
-
JNIでネイティブメソッドスタックオブジェクト参照(すなわち、一般的には、方法ネイティブ前記)。
7.2ガベージコレクションアルゴリズム
後に、スパムのような物体を識別することができて、次に考慮すべきことは、それを回復する方法、回復のですか?対応するアルゴリズムを持っている必要があり、ここでは一般的なガベージコレクションアルゴリズムです。
7.2.1マーク - クリア(マークスイープ)
タグ:メモリ内のオブジェクトを回復する必要が見つけ、そしてそれらをマークします。
このヒープにすべてのオブジェクトが再度スキャンされ、したがって、オブジェクトを決定することができるより多くの時間がかかり、回収されます。
クリア:クリアオブジェクトがメモリ空間のリリースに対応し、リカバリする必要がマークされています。
短所:
あなたが実行されているプログラムの中で大きなオブジェクトを割り当てる必要があり、将来的にあまりにも多くの時間を引き起こす可能性があるスペースデブリをクリアした後に個別のメモリの断片化のマークを大量に生成されます、十分な連続したメモリを検索し、事前に別のガーベッジコレクション動作をトリガしなければならなかったことはできません。
(1)マークと2つのプロセスは時間がかかり、非効率的でスイープ
(2)会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
7.2.2 复制(Copying)
将内存划分为两块相等的区域,每次只使用其中一块,如下图所示:
当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次清除掉。
缺点:空间利用率降低。
7.2.3 标记-整理(Mark-Compact)
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都有100%存活的极端情况,所以老年代一般不能直接选用这种算法。标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
其实上述过程相对"复制算法"来讲,少了一个"保留区"
让所有存活的对象都向一端移动,清理掉边界意外的内存。
7.3 分代收集算法
既然上面介绍了3中垃圾收集算法,那么在堆内存中到底用哪一个呢?
P72分代收集算法
为了增加垃圾回收的效率,JVM会根据对象存活周期的不同将内存分为几块,堆中分为新生代和老年代。这样可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用"标记-清除"或者"标记-整理"算法来进行回收。
小结:
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)
7.4 垃圾收集器
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
7.4.1 Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代收集的唯一选择。
它是一种单线程收集器,不仅仅意味着它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在进行垃圾收集的时候需要暂停其他线程。
优点:简单高效,拥有很高的单线程收集效率
缺点:收集过程需要暂停所有线程
算法:复制算法
适用范围:新生代
应用:Client模式下的默认新生代收集器
7.4.2 ParNew收集器
可以把这个收集器理解为Serial收集器的多线程版本。
优点:在多CPU时,比Serial效率高。
缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
算法:复制算法
适用范围:新生代
应用:运行在Server模式下的虚拟机中首选的新生代收集器
7.4.3 Parallel Scavenge收集器
Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,但是Parallel Scanvenge更关注`系统的吞吐量`。
-
吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)
-
比如虚拟机总共运行了100分钟,垃圾收集时间用了1分钟,吞吐量=(100-1)/100=99%。
-
若吞吐量越大,意味着垃圾收集的时间越短,则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。
-XX:MaxGCPauseMillis控制最大的垃圾收集停顿时间,
-XX:GCRatio直接设置吞吐量的大小。
7.4.4 Serial Old收集器
Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用"标记-整理算法",运行过程和Serial收集器一样。
7.4.5 Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和"标记-整理算法"进行垃圾回收。
吞吐量优先
7.4.6 CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取`最短回收停顿时间`为目标的收集器。
采用的是"标记-清除算法",整个过程分为4步
(1)初始标记 CMS initial mark
标记GC Roots能关联到的对象 Stop The World--->速度很快
(2)并发标记 CMS concurrent mark
进行GC Roots Tracing
(3)重新标记 CMS remark
修改并发标记因用户程序变动的内容 Stop The World
(4)并发清除 CMS concurrent sweep
由于整个过程中,并发标记和并发清除,收集器线程可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。
优点:并发收集、低停顿
缺点:产生大量空间碎片、并发阶段会降低吞吐量
7.4.7 G1收集器
G1收集器在JDK 7正式作为商用的收集器。与前几个收集器相比,G1有以下特点
P84 G1收集器
-
并行与并发
-
分代收集(仍然保留了分代的概念)
-
空间整合(整体上属于“标记-整理”算法,不会导致空间碎片)
-
可预测的停顿(比CMS更先进的地方在于能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒)
使用G1收集器时,Java堆的内存布局与就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
工作过程可以分为如下几步:
-
初始标记(Initial Marking)
标记以下GC Roots能够关联的对象,并且修改TAMS的值,需要暂停用户线程
-
并发标记(Concurrent Marking)
从GC Roots进行可达性分析,找出存活的对象,与用户线程并发执行
-
最终标记(Final Marking)
修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需暂停用户线程
-
筛选回收(Live Data Counting and Evacuation)
对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划
7.4.8 垃圾收集器分类
-
串行收集器->Serial和Serial Old
只能有一个垃圾回收线程执行,用户线程暂停。`适用于内存比较小的嵌入式设备`。
-
并行收集器[吞吐量优先]->Parallel Scanvenge、Parallel Old
多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。`适用于科学计算、后台处理等若交互场景`。
-
并发收集器[停顿时间优先]->CMS、G1
用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集线程在执行的时候不会停顿用户线程的运行。`适用于相对时间有要求的场景,比如Web`。
7.4.9 常见问题
吞吐量和停顿时间
停顿时间->垃圾收集器 `进行` 垃圾回收终端应用执行响应的时间
吞吐量->运行用户代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间越短就越适合需要和用户交互的程序,良好的响应速度能提升用户体验;
高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
小结:这两个指标也是评价垃圾回收器好处的标准,其实调优也就是在观察者两个变量。
如何选择合适的垃圾收集器
* 优先调整堆的大小让服务器自己来选择
* 如果内存小于100M,使用串行收集器
* 如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
* 如果允许停顿时间超过1秒,选择并行或JVM自己选
* 如果响应时间最重要,并且不能超过1秒,使用并发收集器
对于G1收集
JDK 7开始使用,JDK 8非常成熟,JDK 9默认的垃圾收集器,适用于新老生代。
是否使用G1收集器?
(1)50%以上的堆被存活对象占用
(2)对象分配和晋升的速度变化非常大
(3)垃圾回收时间比较长
如何开启需要的垃圾收集器这里JVM参数信息的设置大家先不用关心,下一章节会详细写到。
(1)串行
-XX:+UseSerialGC
-XX:+UseSerialOldGC
(2)并行(吞吐量优先):
-XX:+UseParallelGC
-XX:+UseParallelOldGC
(3)并发收集器(响应时间优先)
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC