Java仮想マシンを学ぶ - ガベージコレクション
まず、ガベージコレクションは何ですか
Javaは自動的にメモリの実装プログラムのロジックを使用することがより安全で便利なトップクラスのビジネスを作り、管理を取り戻すためにメモリを割り当てます。異なるJVM実装と異なる回収機構において、ヒープメモリの分割は、同じ方法ではありません。主な目的は、自動的にメモリを解放、使用されなくなっガベージコレクションオブジェクトをクリアすることではありません。
第二に、どのようにオブジェクトを再利用する必要があるかを決定します
到達可能性解析:
GCルーツ。オブジェクトは、任意のオブジェクト参照の都市エリアとしてGCのルーツとの関係、に直接又は間接参照を持っていない、または二つの相互に取り付けられた円形の参照オブジェクトを使用する場合。
GCのルーツがそうであるようにどのようなオブジェクトを使用することができますか?たとえば、次のオブジェクトリテラルの参考文献に参照されるオブジェクトクラスの静的プロパティは、仮想マシンのスタックに参照されるオブジェクト、ネイティブメソッドスタックは、オブジェクトを参照します。
参照の4種類:
a)の強い参照。
新しいオブジェクトを生成し、JVM必須ではありません、リサイクルなどのオブジェクト。
b)のソフト参照。
参考ではなく、本来の目的。メモリのオーバーフローが発生する前に、これらのオブジェクトをもう一度復元してみてください。SoftReferenceクラス。
参照ソフト参照オブジェクトがガーベッジである場合、一般に、メモリに敏感なキャッシュを実装するために使用される。ソフト参照と組み合わせて使用される参照コホート(ReferenceQueue)、JAVA仮想マシンはそれが関連付けられたキューを挙げソフト参照に追加されます(SoftCache MyBatisのキャッシュモジュールを使用しています)
c)に弱いです。
非必須オブジェクト。ガベージコレクタの仕事は、関係なく、現在のメモリーの妥当性の回復します。弱い参照クラスの実装。
弱参照は、はるかに短いライフサイクルよりも軟質で引用。これはソフト参照に類似しており、参照キュー(RefrenceQueue)を使用し、ガベージコレクタによって参照される弱参照オブジェクトは、Java仮想マシンがこの弱参照に追加された場合とすることができます関連する参照キュー。
d)の仮想基準。
オブジェクトがコレクタに回収されたときに通知を受け取るために、システムに関連付けられているオブジェクトの参照を設定する唯一の目的。PhantomReferenceクラスが実装します。
それが見つかった場合、ターゲットを製造するために組み合わせリサイクルガベージコレクタで使用される場合、仮想基準および基準(RefrenceQueeu)をキューイングしなければいけません:仮想オブジェクト参照は、主に廃ファントム参照を回収し、その中で引用されたソフトと弱参照の差が活動を追跡するために使用されそれはまた、徐参照がメモリの前に回収される、この参照は、参照に関連付けられた仮想キューに追加されます。徐は、このプログラムは、学ぶために参照し、参照に関連付けられたキューに追加され参照することができますオブジェクトは、ガベージコレクタであることがあります。
第三に、時に回復
1)列挙ルート
ルートノードの到達可能性解析を列挙すると、オブジェクト参照が現象の関係を変更しないことを確認する必要がありそうでない場合、我々は分析結果の正確性を保証することはできません、分析の過程にまだある。そのため、GCの行動は、すべての実行スレッドを停止する必要があります(世界、STWを停止します)。
休止時のHotSpot仮想マシン、直接オブジェクト参照を格納するためにどの場所を知ることOopMapからなるデータ構造を使用して、グローバル実行コンテキストと場所、時間を浪費を減らすことへのすべての参照をチェックする必要はありません。
2)安全ポイント
各命令OopMap発生しないホットスポットは、空間の損失を低減するために、唯一の位置が安全点(セーフポイント)と呼ばれ、「指定位置」に情報を記録します。
プログラムの選択された安全性の点で基本的には「長時間実行プログラムを作成するかどうかの機能の標準として選択」長時間実行される最も明白な特徴は、例えば、命令多重化のシーケンスである」、メソッド呼び出しディアンループジャンプディアンら異常なジャンプ命令は、これらの機能は、セーフポイントが生成されますがあります。
GCは、(JNI呼び出したスレッドは含まない)すべてのスレッドは、最寄りの安全なポイントに「実行」された後、停止に来て発生したときにする方法。そこプリエンプティブ割り込み(プリエンプティブサスペンション)とアクティブな割り込み(Voluntartyサスペンション)。プリエンプティブ割り込みGCが発生したときに、スレッドを再開するために、安全なポイントを持っていないスレッドを見つけた場合、すべての混乱のすべてのスレッドの最初の、それ安全ポイントに「ファイル名を指定して実行」しましょう。しかし、今の仮想マシンは、一般的にアクティブな割り込みを使用している。ときスレッドを中断するために必要なGC時間は、スレッドは直接各スレッド実行イニシアチブが回転でこれを識別するときだけで、簡単なアイデンティティを設定し、動作しない、そして中断自分自身を中断し、割り込みフラグが真であることがわかりました。
3)セキュリティドメイン
安全な場所に割り込み要求手順が実行されていない、「歩行」に対応していないJVMは、保留中の割り込み。(行いません、CPU時間のない割り当てが存在しないことを、典型的な例は、スレッドでスリープ状態またはブロックされた状態になっています)
これは、地域の安全性に対処するためのセキュリティゾーン(SafeRegion)を必要とするときであるコードフラグメントの一部である、参照関係が変化しない、この領域の任意の場所にGCが安全で始まりました。
その領域内のスレッドセーフなコードで実行するには、まず、彼らは安全な領域に入ったことを確認することである。GCは、この期間中に発生した場合は、スレッドの安全な領域に入ったものを制御しません。スレッドが安全なエリアを離れるときは、システムをチェックすることですかどうか、ルートノードの列挙が完了し、完了した場合、信号の安全な領域を残しても安全になるまで、スレッドは続けて、それ以外の場合は待機します。
第四に、どのようにリサイクルします
4.1)ガベージコレクションアルゴリズム
a)は明確なラベリングアルゴリズム---古い一般のために、より多くのスペースデブリが生成されます
最も基本的なコレクションアルゴリズム、その後のアルゴリズムは二段階でその「マーク」と「クリア」にこのアイデアを向上させることに基づいています:最初のマークのすべてのオブジェクトを回復する必要がある、すべてのマークの完了をマーキング後に回収統一オブジェクト。
有两个不足, 一是效率问题, 标记和清除两个过程效率都不高,另一个是空间问题,标记清除之后会产生大量不连续的内存碎片.空间碎片太多可能倒置以后在程序运行过程中需要分配较大对象时,找不到足够的连续内存而不得不提前触发另一个垃圾回收.
b) 标记复制算法-- 一般用于新生代。
将可用内存按容量划分为大小相等的凉快, 每次只使用其中的一块.当这一块内存用完之后,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉. 这样使得每次都是对整个半区进行内存回收, 内存分配时, 也就不用考虑内存碎片等复杂情况.
现在的虚拟机都是采用这种收集算法来回收新生代. 将内存分为一块较大的Eden空间和两块较小的Survivor空间, 每次使用Eden和其中一块Survivor. 每当回收时, 就讲还存活的对象一次性复制到另一块Survivor空间上, 最后清理掉Eden和刚才用过的Survivor空间.
HotSpot虚拟机默认Eden和Survivor的大小是8:1. 由于新生代存活率较低, 复制代价较低, 并且只浪费10%的内存空间, 还是可以接受的.
d) 标记整理算法 -- 这个用于老年代。
老年代存活率较高,如果使用复制算法, 则复制代价比较高, 老年代一般使用"标记整理"算法, 这种算法和"标记清除"算法类似, 但标记之后, 并不是清除, 而是将存活的对象复制移到到一端, 然后将端边界外的对象清除掉.
e) 分代收集算法
根据对象存活周期的不同将内存划分为几块, 一般是把java堆划分为新生代和老年代, 然后根据各年代的特点采用最适当的收集算法.
在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,那就选用复制算法
在老年代中,一般存活率较高,一般选用清除算法。
4.2) 垃圾收集器
4.2.1) 新生代
1) Serial收集器 (串行)
该收集器是只有一条线程的收集器, 垃圾回收时,必须停掉其他的线程, 即是发生STW(Stop the World)。适用于单个CPU的环境, 适用于桌面应用场景(client模式)
2)ParNew收集器(并发)
Serial的并发实现, 该收集器的控制参数丶对象分配丶回收策略等等都和Serial收集器一样.在JDK1.5中, 老年代使用CMS收集器时, 新生代只能搭配Serial或ParNew收集器中的一个. ParNew收集器是使用CMS收集器时(-XX: UseConcMarkSweepGC)默认的新生代收集器.
3)Parallel Scavenge收集器(并发)
该收集器的关注点和其他收集器的不同, CMS等收集器的关注点在于尽量缩短垃圾回收的停顿时间, 而这个收集器的目的是为了提供更大的吞吐量,如虚拟机运行100分钟,垃圾收集用了1分钟,它的吞吐量为99%。
停顿时间越短, 越适合与用户交互的程序, 良好的响应速度能够提升用户体验. 而高吞吐量能够高效率的利用CPU时间,尽快的完成计算任务, 主要适合在后台计算而无须用户过多交互的任务.
4.2.2) 老年代
4)Serial Old收集器(串行)
Serial Old收集器是Serial收集器的老年代版. 它是一个单线程的收集器, 使用"标记-整理"算法.
主要作用: 1. Client模式使用 2. Server模式, 搭配新生代Parallel Scavenge收集器使用. 3. 作为CMS的备选方案
5) Parallel Old收集器(并发)
Parallel Old收集器是Parallel Scavenge收集器的老年代版本, 使用多线程和"标记-整理"算法. 该收集器是JDK1.6版本才提供的, JDK 1.5 提供的Parallel New收集器, 由于Serial Old的"拖累", 一直无法发挥"最大吞吐量"的优势, 直到1.6提供的Parallel Old收集器才是名副其实.
6) CMS收集器(并发)
CMS(Concurrent Mark Sweep)收集器是一种以最短回收停顿时间为目标的垃圾收集器.
CMS是基于"标记-清除"算法的, 分为一下四个步骤:
a. 初始标记(STW)
b. 并发标记
c. 重新标记(STW)
d. 并发清除
三个缺点:
a. 对CPU资源比较敏感.在并发阶段, 它不会导致用户线程停顿, 但它会因占用一部分线程而导致应用程序变慢. CMS默认启动的回收线程数为(CPU数量+3)/4
b. CMS处理器无法处理浮动垃圾. CMS并发标记清除阶段, 用户线程还在运行, 可能产生垃圾. 这些垃圾被成为"浮动垃圾".所以, 在进行垃圾回收时,也需要预留一些空间供给用户线程使用. JDK 1.5 默认设置, 当老年代使用了68%时, 就会触发GC. 如果应用程序老年代增长不会很快,可以使用-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比, 以降低内存回收次数从而获取更好的性能. JDK1.6, 启动阀值设置为92%. 如果CMS运行期间预留的内存无法满足程序需要, 就会出现一次"Concurrent Mode Failure" 失败, 这时虚拟机讲启动后背源: 临时启用Serial Old收集器来重新进行老年代的垃圾收集, 使得停顿时间更长了. 启动阀值设置过高, 很容器出现失败, 导致性能降低.
c. 标记清除算法,会产生大量空间碎片。所以需要空间整理,而空间整理会产生STW
可以使用-XX:+UseCMSCompactAtFullCollection参数来强制JVM在FGC完成后对老年代进行压缩,执行一次空间碎片整理。为减少STW的次数, 可以使用--XX:+CMSFullGCsBeforeCompaction=n参数,在执行了n次FGC后,JVM在在老年代执行空间碎片整理。
7) G1收集器
G1是一款面向服务端应用的垃圾收集器.
与其他GC收集器相比, G1具备如下特点:
a) 并行与并发: G1 能充分利用多CPU丶多核环境下的硬件优势, 使用多个CPU来缩短STW挺短时间.
b) 分代收集: 可以独立管理整个GC堆.
c) 空间整合: 从整体上看, 是基于"标记-整理"算法实现的收集器, 从局部(两个Region之间)上来看是基于"复制"算法实现的.G1运作期间不会产生内存空间碎片, 收集后能提供规整的可用内存, 利于程序长时间运行.
d) 可预测的停顿: 这是G1相对于CMS的另一大优势, 降低停顿时间是G1和CMS共同的关注点, 但G1除了追求低停顿之外, 还能建立可预测的停顿时间模型, 能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒, 这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了.
G1收集器大概分为以下几个步骤:
a) 初始标记
b) 并发标记
c) 最终标记
d) 筛选回收
五丶参数总结
学习资料:
<深入理解java虚拟机>
<精尽 Java 虚拟机 | 芋道源码>