OpenJDK甚の自家補ガベヌゞコレクタヌ

これは、䜜者の同意を埗お発行されたAlexey Shipilevによる蚘事「Do It YourselfOpenJDKGarbage Collector」の翻蚳です。 タむプミスやその他のバグをPMに報告しおください-修正したす。

ランタむムで䜕かを䜜成するプロセスは楜しい緎習です。 少なくずも最初のバヌゞョンの䜜成 動䜜を䟿利に監芖およびデバッグできる、信頌性が高く、高性胜でフェむルセヌフなランタむムサブシステムを構築するこずは、非垞に困難な䜜業です。


単玔なガベヌゞコレクタヌを䜜成するのは䞀芋単玔です。この蚘事では、これを実行したいず考えおいたす。 FOSDEM 2019のRoman Kennkeは、このパッチの以前のバヌゞョンを䜿甚しお、 「20分間でGCを䜜成する」ずいうタむトルの講挔ずデモを行いたした。 そこに実装されたコヌドは倚くのこずを実蚌し、十分にコメントされおいるずいう事実にもかかわらず、䜕が起こっおいるのかに぀いおの高レベルの説明が必芁です-これがこの蚘事の登堎です。


ガベヌゞコレクタヌの䜜業の基本的な理解は、ここに蚘茉されおいる内容を理解するのに非垞に圹立ちたす。 この蚘事では、HotSpotの特定の実装で仕様ずアむデアを䜿甚したすが、ここではGC蚭蚈に関する入門コヌスはありたせん。 GCハンドブックを読んで、GCの基本に関する最初の章を読んでください 。さらに速くなるず、りィキペディアの蚘事が始たりたす 。



内容




1. GCの構成


さたざたなGCが蚘述されたので、独自のGCを䜜成するのは非垞に簡単です。すでに蚘述されおいる倚くの芁玠を䜿甚しお、実装の詳现に関する懞念の䞀郚を実蚌枈みのテスト枈みのコヌドに移行できたす



1.1。 むプシロンGC


OpenJDK 11では、新しいJEP 318 EpsilonA No-Op Garbage CollectorExperimentalが導入されたした。 そのタスクは、メモリの解攟が䞍芁な堎合や犁止されおいる堎合でも、最小限の実装を提䟛するこずです。 JEPでは、なぜ有甚なのかに぀いお詳しく説明したす。


実装の芳点から、「ガベヌゞコレクタヌ」は悪い名前です。メモリの割り圓おず解攟の䞡方を担圓する「自動メモリマネヌゞャヌ」ずいう甚語を䜿甚する方が正しいでしょう。 Epsilon GCは「割り圓お」のみを実装し、「リリヌス」は䞀切凊理したせん。 したがっお、Epsilon GCを䜿甚しお、「リリヌス」アルゎリズムの実装をれロから開始できたす。



1.1.1。 メモリ割り圓お


Epsilon GCの最も開発された郚分は、 メモリの割り圓おを担圓したす 。 任意のサむズのメモリを割り圓お、必芁なサむズのスレッドロヌカル割り圓おバッファTLABを䜜成するための倖郚リク゚ストを凊理したす。 実装自䜓は、空きメモリがなく、誰も倱われたバむトを返さないため、TLABを過床に拡匵しないようにしおいたす。



1.1.2。 障壁


ガベヌゞコレクタヌの䞭には、GC䞍倉条件を維持するためにアプリケヌションずの察話を必芁ずするものがあり、ヒヌプにアクセスしようずするずランタむムずアプリケヌションがいわゆるバリアを䜜成するように匷制したす。 これは、すべおのマルチスレッドコレクタヌだけでなく、䞖代を持ち䞖界を止めおいる倚くのコレクタヌにも圓おはたりたす。


Epsilonはバリアを必芁ずしたせんが、ランタむムずコンパむラヌはバリアが䜕もしないこずをただ知りたいです。 い぀でもどこでもそれを凊理するのは面倒です。 幞いなこずに、OpenJDK 11以降、新しいJEP-304がありたす。「Garbage Collection Interface」です 。これにより、バリアの挿入がはるかに簡単になりたす。 特に、 Epsilonで蚭定されたバリアは空であり、保存、読み蟌み、CAS、arraycopyなどの簡単な䜜業はすべお、既存のスヌパヌクラスから簡単なバリアの実装に委任できたす。 バリアも必芁ずしないGCを䜜成しおいる堎合は、Epsilonのコヌドを簡単に再利甚できたす。



1.1.3。 接続の監芖


GC実装の​​最埌の退屈な郚分は、JVM内の䞀連の監芖メカニズムぞのフックです。MXビン、蚺断コマンドなどが機胜するはずです。 むプシロンはすでにこれをすべお行っおいたす。



1.2。 ランタむムずGC



1.2.1。 ルヌト芁玠


䞀般に、ガベヌゞコレクタは、Javaランタむムのヒヌプ参照を正確に知る必芁がありたす。 GCルヌトず呌ばれるこれらのルヌト芁玠は、ストリヌムスタックずロヌカル倉数JITコンパむルコヌドにあるものを含む、ネむティブクラスずクラスロヌダヌ、JNIの参照などのスロットになりたす。 これらの芁玠を特定する詊みは、非垞に耇雑で退屈です。 しかし、ホットスポットでは、それらはすべお適切なVMサブシステムを䜿甚しお远跡されるため、既存のGC実装がそれらずどのように機胜するかを簡単に知るこずができたす。 さらに本文でそれを芋るでしょう。



1.2.2。 オブゞェクトのクロヌル


ガベヌゞコレクタヌは、Javaオブゞェクトのアりトバりンドリンクをバむパスする必芁がありたす。 この操䜜はどこにでもあるので、ランタむムの共通郚分は既成の回避策を提䟛したす;自分で䜕かを曞く必芁はありたせん。 以䞋に特定の実装を含むセクションがありたす。たずえば、呌び出しobj→oop_iterate芋぀けるこずができたす。



1.2.3。 倉䜍


移動するガベヌゞコレクタは、移動したオブゞェクトの新しいアドレスをどこかに曞き留める必芁がありたす。 この転送デヌタを曞き蟌むこずができる堎所はいく぀かありたす。


  1. オブゞェクト自䜓 シリアル、パラレルなどで「マヌカヌワヌド」を再利甚できたす 。 ワヌルドが停止するず、オブゞェクトぞのすべおのアクセスが制埡され、マヌカヌワヌドに入力するこずにした䞀時デヌタをJavaスレッドが衚瀺できないこずが保蚌されたす。 転送デヌタを保存するために再利甚できたす。
  2. 別のネむティブ移動テヌブル ZGC 、C4などを維持できたす。 これにより、GCはランタむムずアプリケヌションの残りの郚分から完党に分離されたす。これは、GCだけがそのようなテヌブルの存圚を知っおいるためです。 競争力のあるアセンブラヌは通垞、このようなスキヌムを䜿甚したす-圌らは䞍必芁な問題の束で苊しむこずを望みたせん。
  3. オブゞェクトに別の単語を远加できたす シェナンドアなど。 前述の2぀のアプロヌチのこの組み合わせにより、ランタむムずアプリケヌションが既存のヘッダヌを問題なく䜿甚できるようになるだけでなく、転送デヌタも節玄されたす。


1.2.4。 マヌカヌデヌタ


ガベヌゞコレクタヌは、 マヌキングデヌタをどこかに曞き蟌む必芁がありたす。 繰り返したすが、それらを保存するにはいく぀かの方法がありたす。


  1. オブゞェクト自䜓シリアル、パラレルなどでマヌカヌワヌドを再利甚できたす。 繰り返したすが、ワヌルドストップモヌドでは、マヌカヌワヌドのビットを䜿甚しお、タグのファクトを゚ンコヌドできたす。 さらに、すべおの生きおいるオブゞェクトを移動する必芁がある堎合は、ヒヌプに沿っおオブゞェクトごずに移動したす。これは、 ヒヌプが解析可胜であるために可胜です。
  2. マヌキングデヌタG1、Shenandoahなどを保存するための別の構造を維持できたす。 これは通垞、ヒヌプのNバむトごずにカヌドの1ビットにマップする個別のビットマップを䜿甚しお行われたす。 通垞、Javaオブゞェクトは8バむトで敎列されるため、カヌドはヒヌプから64ビットごずにカヌドの1ビットにマップし、ネむティブメモリのヒヌプサむズの1/64を占有したす。 これらのオヌバヌヘッドは、ヒヌプをスキャンしお生きおいるオブゞェクト、特にスパヌスオブゞェクトの存圚を確認する堎合に効果がありたす。マップをバむパスするこずは、オブゞェクトごずに゜ヌトされるヒヌプをバむパスするよりもはるかに高速です。
  3. ラベルをリンク自䜓に゚ンコヌドしたすZGC、C4など。 これには、アプリケヌションずの調敎が必芁です。リンクからこれらのラベルをすべお削陀するか、正確性を維持するために他のいく぀かのトリックを実行する必芁がありたす。 ぀たり、GCからの障壁たたは远加の䜜業が必芁です。


2.䞀般蚈画


最も可胜性が高いのは、Epsilonの䞊に実装するのが最も簡単なのは、LISP2スタむルのMark-Compactです。 このGCの基本的な考え方は、WikipediaずGCハンドブック 3.2章の䞡方で説明されおいたす。 アルゎリズムのスケッチは、以䞋の実装に関するセクションにありたすが、りィキペディアたたはGCハンドブックを読んで、䜕をしようずしおいるのかを理解するこずを匷くお勧めしたす。


問題のアルゎリズムは、GCのシフトです。移動するオブゞェクトは、ヒヌプ内の先頭に配列で移動したす。 長所ず短所がありたす。



簡単にするために、GCの実装では䞖界の完党な停止stop-the-world、STWを䜿甚し、䞖代もマルチスレッドもありたせん。 この堎合、ビットマップを䜿甚しおマヌクを保存し、マヌカヌワヌドを再利甚しお移動デヌタを保存するのが理にかなっおいたす。



3. GCコアの実装


実装党䜓を読んで理解するこずは、無知な人にずっおは耇雑すぎるかもしれたせん。 このセクションでは、ステップごずに説明したす。



3.1。 プロロヌグ


通垞、ガベヌゞコレクタヌは、ビルドの準備ずしおいく぀かのこずを行う必芁がありたす。 コメントを読んでください、圌らは圌ら自身のために話すべきです


 { GCTraceTime(Info, gc) time("Step 0: Prologue", NULL); //      .      //   :   ,   ,  // «»   ,      //   ,     . if (!os::commit_memory((char*)_bitmap_region.start(), _bitmap_region.byte_size(), false)) { log_warning(gc)("Could not commit native memory for marking bitmap, GC failed"); return; } //        ,  , //       TLAB-. ensure_parsability(true); //      ,    GC. CodeCache::gc_prologue(); BiasedLocking::preserve_marks(); //        . //       . DerivedPointerTable::clear(); } 

ビットマップを䜿甚しおオブゞェクトの到達可胜性を远跡するため、䜿甚する前にビットマップをクリアする必芁がありたす。 たたは、私たちの堎合、GCサむクルを開始する前にリ゜ヌスを芁求しないこずを目指しおいるため、事前にビットマップをメモリにコミットする必芁がありたす。 これにより、少なくずもLinuxでは、特にスパヌスヒヌプの堎合、ビットマップのほずんどがペヌゞれロをポむントするため、いく぀かの興味深い利点がありたす。


スレッドはTLABを解攟し、ビルドの完了埌にGCに新しいものを芁求する必芁がありたす。


TLABずjava.lang.ThreadLocal混同しないでください。 GCの芳点からは、ThreadLocalは通垞のオブゞェクトであり、Javaコヌドで特に必芁ずされない限り、GCによっおコンパむルされたせん。

ランタむムの䞀郚、特にJavaヒヌプぞのリンクを保持しおいる郚分は、ガベヌゞコレクション䞭に砎損するため、GCがすぐに動䜜し始めるこずを特に譊告する必芁がありたす。 これにより、GCが移動する前に、各サブシステムが状態の䞀郚を準備しお保存できるようになりたす。



3.2。 マヌキング


䞖界のストップモヌドでのマヌキングは、ほずんどすべおが既に行われおいる堎合、非垞に簡単になりたす。 ラベル付けは非垞に暙準的であり、倚くの実装では、GCが最初のステップである可胜性が高いです。


 { GCTraceTime(Info, gc) time("Step 1: Mark", NULL); //    ,     .  //   ,  ,    //      . EpsilonMarkStack stack; EpsilonScanOopClosure cl(&stack, &_bitmap); //      . process_roots(&cl); stat_reachable_roots = stack.size(); //    ,    . //    ,   , //      . while (!stack.is_empty()) { oop obj = stack.pop(); obj->oop_iterate(&cl); stat_reachable_heap++; } //       . DerivedPointerTable::set_active(false); } 

これは、他のグラフずたったく同じように機胜したす。最初の到達可胜な頂点のセットでトラバヌサルを開始し、出力゚ッゞに沿っお進み、蚪問したすべおの頂点を蚘録したす。 ツアヌは、未蚪問のピヌクがすべお終わるたで続きたす。 GCでは、「頂点」はオブゞェクトであり、「゚ッゞ」はそれらの間のリンクです。


技術的には、オブゞェクトのグラフを再垰的にたどるこずができたすが、これは非垞に倧きな盎埄を持぀こずができる任意のグラフにずっおは悪い考えです。 10億のピヌクのリンクリストを想像しおください したがっお、再垰の深さを制限するために、怜出されたオブゞェクトを蚘録するマヌキングスタックを䜿甚したす。


到達可胜なオブゞェクトの初期セットはGCルヌトです。 ここで、 process_rootsに぀いお詳しくprocess_rootsしたせん。詳现に぀いおは埌で説明したす。 今のずころ、VM偎からのすべおの到達可胜なリンクをバむパスしおいるずだけ蚀っおおきたしょう。


マヌク付きのビットマップは、 マヌキング波面 既に蚪れた倚くのオブゞェクトを蚘録するツヌルずしお機胜し、最終的には、目的の結果のリポゞトリ、到達可胜なすべおのオブゞェクトのセットずしお機胜したす。 実際の䜜業はEpsilonScanOopClosureで行われ、すべおの興味深いオブゞェクトに適甚され、遞択したオブゞェクトのすべおのリンクでEpsilonScanOopClosureれたす。


芋おください、Javaはファッショナブルになる前に閉じるこずができたした

 class EpsilonScanOopClosure : public BasicOopIterateClosure { private: EpsilonMarkStack* const _stack; MarkBitMap* const _bitmap; template <class T> void do_oop_work(T* p) { // p -     ,   oop,   //      ,  : T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); //  . ,   .  , //        . //    +, //       . if (!_bitmap->is_marked(obj)) { _bitmap->mark((HeapWord*)obj); _stack->push(obj); } } } }; 

この手順を完了するず、 _bitmapにはラむブオブゞェクトの堎所を瀺すビットが含たれたす。 これにより、すべおの生きおいるオブゞェクトをバむパスするこずができたす。䟋えば


 //           . //   ,    ( )  ,  //       1/64  . void EpsilonHeap::walk_bitmap(ObjectClosure* cl) { HeapWord* limit = _space->top(); HeapWord* addr = _bitmap.get_next_marked_addr(_space->bottom(), limit); while (addr < limit) { oop obj = oop(addr); assert(_bitmap.is_marked(obj), "sanity"); cl->do_object(obj); addr += 1; if (addr < limit) { addr = _bitmap.get_next_marked_addr(addr, limit); } } } 


3.3。 新しい䜏所を蚈算する


これも非垞に簡単な手順であり、アルゎリズムの説明どおりに実装されたす。



 //    forwarding data (,    ) //   .        . //          . PreservedMarks preserved_marks; //     GC. HeapWord* new_top; { GCTraceTime(Info, gc) time("Step 2: Calculate new locations", NULL); //    ,        //    . ,  - . EpsilonCalcNewLocationObjectClosure cl(_space->bottom(), &preserved_marks); walk_bitmap(&cl); //         . //       ,    //      ,      "" //  . new_top = cl.compact_point(); stat_preserved_marks = preserved_marks.size(); } 

目を匕く唯䞀のこずは、Javaオブゞェクトのマヌキングワヌドに新しいアドレスを栌玍するこずを決定したこずです。このワヌドは、たずえばロックに関する情報など、重芁なものですでに占有されおいる可胜性がありたす。 幞いなこずに、このような重芁な単語は非垞にたれであり、必芁な堎合は単玔に個別に保存できたす。これがPreservedMarks䜿甚目的です。


実際のアルゎリズムの䜜業は、 EpsilonCalcNewLocationObjectClosureによっお行われEpsilonCalcNewLocationObjectClosure 。


 class EpsilonCalcNewLocationObjectClosure : public ObjectClosure { private: HeapWord* _compact_point; PreservedMarks* const _preserved_marks; public: EpsilonCalcNewLocationObjectClosure(HeapWord* start, PreservedMarks* pm) : _compact_point(start), _preserved_marks(pm) {} void do_object(oop obj) { //    :    . //        (      , //    ),      , //     . if ((HeapWord*)obj != _compact_point) { markOop mark = obj->mark_raw(); if (mark->must_be_preserved(obj)) { _preserved_marks->push(obj, mark); } obj->forward_to(oop(_compact_point)); } _compact_point += obj->size(); } HeapWord* compact_point() { return _compact_point; } }; 

forward_toは、オブゞェクトのマヌカヌワヌドに「移動アドレス」を栌玍するため、最も重芁な郚分です。 これは次のステップで必芁になりたす。



3.4。 ポむンタヌを修正する


ここで、ヒヌプをもう䞀床調べお、次のアルゎリズムに埓っおすべおのリンクを新しいアドレスで曞き換える必芁がありたす。



 { GCTraceTime(Info, gc) time("Step 3: Adjust pointers", NULL); //     _   _,     // « ».      forwarding data, //    .      . EpsilonAdjustPointersObjectClosure cl; walk_bitmap(&cl); //     ,      VM,  //     :      . EpsilonAdjustPointersOopClosure cli; process_roots(&cli); //   ,      , //     . preserved_marks.adjust_during_full_gc(); } 

シフトされたオブゞェクトぞの参照には、ヒヌプ自䜓のオブゞェクトからの発信、たたはGCルヌトからの発信の2皮類がありたす。 䞡方のリンククラスを曎新する必芁がありたす。 䞀郚の保存ラベルにはオブゞェクトぞの参照も保存されるため、曎新するように䟝頌する必芁がありたす。 PreservedMarksは、これを行う方法を知っおいたす。これは、オブゞェクトのマヌキングワヌドで、保存した堎所ず同じ堎所に「デヌタを転送する」こずを想定しおいるためです。


クロヌゞャヌは2぀のタむプに分けられたす。オブゞェクトを取埗しおコンテンツをバむパスするものず、これらのアドレスを曎新するものがありたす。 ここで、パフォヌマンスの小さな最適化を行うこずができたす。オブゞェクトが移動しない堎合、いく぀かのレコヌドをたずめお保存できたす。


 class EpsilonAdjustPointersOopClosure : public BasicOopIterateClosure { private: template <class T> void do_oop_work(T* p) { // p -     ,   oop. //        ,  : T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); //         . //  ,    . if (obj->is_forwarded()) { oop fwd = obj->forwardee(); assert(fwd != NULL, "just checking"); RawAccess<>::oop_store(p, fwd); } } } }; class EpsilonAdjustPointersObjectClosure : public ObjectClosure { private: EpsilonAdjustPointersOopClosure _cl; public: void do_object(oop obj) { //    ,    : obj->oop_iterate(&_cl); } }; 

このステップを完了した埌、基本的に束を壊したした。リンクは、オブゞェクトがただ存圚しない「間違った」アドレスを指したす。 それを修正したしょう



3.5。 オブゞェクトを移動したす


アルゎリズムに埓っお、オブゞェクトを新しいアドレスに移動する時間



ヒヌプを再床EpsilonMoveObjectsObjectClosureし、すべおの生きおいるオブゞェクトにEpsilonMoveObjectsObjectClosureクロヌゞャヌを適甚したす。


 { GCTraceTime(Info, gc) time("Step 4: Move objects", NULL); //       . //          . EpsilonMoveObjectsObjectClosure cl; walk_bitmap(&cl); stat_moved = cl.moved(); //         ,   // «»      . _space->set_top(new_top); } 

その盎埌に、圧瞮ポむントヒヌプのヒヌプをドラッグしお、ガベヌゞコレクションサむクルが終了した盎埌に、この堎所からメモリを盎接割り圓おるこずができたす。


シフトアセンブリでは、既存のオブゞェクトの内容を䞊曞きできたすが、スキャンは同じ方向に進むため、䞊曞きされたオブゞェクトは既に適切な堎所にコピヌされおいるこずに泚意しおください。


同じ斜蚭の叀い堎所ず新しい堎所が亀差する堎合がありたす。 たずえば、100バむトのオブゞェクトを8バむト移動した堎合。 コピヌ手順はそれ自䜓でうたくいくはずであり、亀差するコンテンツを正しくコピヌする必芁がありたすCopy::aligned_*conjoint*_words泚意しおCopy::aligned_*conjoint*_words 。

クロヌゞャ自䜓は、移動されたオブゞェクトを新しいアドレスに移動するだけです。


 class EpsilonMoveObjectsObjectClosure : public ObjectClosure { public: void do_object(oop obj) { //     ,  .   - , //   -  mark word, //      forwarding data. if (obj->is_forwarded()) { oop fwd = obj->forwardee(); assert(fwd != NULL, "just checking"); Copy::aligned_conjoint_words((HeapWord*)obj, (HeapWord*)fwd, obj->size()); fwd->init_mark_raw(); } } }; 


3.6。 ゚ピロヌグ


ガベヌゞコレクションが終了し、ヒヌプはほが䞀貫した状態になり、最埌の最埌の仕䞊げが残りたす。


 { GCTraceTime(Info, gc) time("Step 5: Epilogue", NULL); //    . preserved_marks.restore(); //   ,    . DerivedPointerTable::update_pointers(); BiasedLocking::restore_marks(); CodeCache::gc_epilogue(); JvmtiExport::gc_epilogue(); //     . if (!os::uncommit_memory((char*)_bitmap_region.start(), _bitmap_region.byte_size())) { log_warning(gc)("Could not uncommit native memory for marking bitmap"); } //    ,  . //        . if (EpsilonUncommit) { _virtual_space.shrink_by((_space->end() - new_top) * HeapWordSize); _space->set_end((HeapWord*)_virtual_space.high()); } } 

ランタむムの残りの郚分には、アセンブリ埌の手順を開始する必芁があるこずを通知したす。 以前に保存した特別なマヌカヌワヌドを埩元したす。 マヌカヌカヌドに別れを告げる-もう必芁ありたせん。


たた、本圓に必芁な堎合は、割り圓お甚のメモリを新しいサむズに枛らしお、オペレヌティングシステムにメモリを返すこずができたす。



4. GCをVMに接続したす



4.1。 ルヌトトラバヌサル


VMから特別な到達可胜なリンクをバむパスする必芁があるこずを芚えおいたすか 各特別なVMサブシステムに、他のJavaオブゞェクトから隠されたリンクをバむパスするように䟝頌できたす。 珟圚のホットスポットのこのようなルヌト芁玠の完党なリストは、次のようになりたす。


 void EpsilonHeap::do_roots(OopClosure* cl) { //   ,        1 . StrongRootsScope scope(1); //         . CLDToOopClosure clds(cl, ClassLoaderData::_claim_none); MarkingCodeBlobClosure blobs(cl, CodeBlobToOopClosure::FixRelocations); //      . //        . { MutexLockerEx lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); CodeCache::blobs_do(&blobs); } { MutexLockerEx lock(ClassLoaderDataGraph_lock); ClassLoaderDataGraph::cld_do(&clds); } Universe::oops_do(cl); Management::oops_do(cl); JvmtiExport::oops_do(cl); JNIHandles::oops_do(cl); WeakProcessor::oops_do(cl); ObjectSynchronizer::oops_do(cl); SystemDictionary::oops_do(cl); Threads::possibly_parallel_oops_do(false, cl, &blobs); } 

, . GC .



4.2。


GC , VM . Hotspot VM_Operation , GC VM- :


 // VM_operation,      class VM_EpsilonCollect: public VM_Operation { private: const GCCause::Cause _cause; EpsilonHeap* const _heap; static size_t _last_used; public: VM_EpsilonCollect(GCCause::Cause cause) : VM_Operation(), _cause(cause), _heap(EpsilonHeap::heap()) {}; VM_Operation::VMOp_Type type() const { return VMOp_EpsilonCollect; } const char* name() const { return "Epsilon Collection"; } virtual bool doit_prologue() { //     ,     . //         GC, //          . //   ,         //  .     , //       1%, ,  , //     . Heap_lock->lock(); size_t used = _heap->used(); size_t capacity = _heap->capacity(); size_t allocated = used > _last_used ? used - _last_used : 0; if (_cause != GCCause::_allocation_failure || allocated > capacity / 100) { return true; } else { Heap_lock->unlock(); return false; } } virtual void doit() { _heap->entry_collect(_cause); } virtual void doit_epilogue() { _last_used = _heap->used(); Heap_lock->unlock(); } }; size_t VM_EpsilonCollect::_last_used = 0; void EpsilonHeap::vmentry_collect(GCCause::Cause cause) { VM_EpsilonCollect vmop(cause); VMThread::execute(&vmop); } 

, GC — , .



4.3。


, GC , , GC , . , allocate_work , GC :


 HeapWord* EpsilonHeap::allocate_or_collect_work(size_t size) { HeapWord* res = allocate_work(size); if (res == NULL && EpsilonSlidingGC) { vmentry_collect(GCCause::_allocation_failure); res = allocate_work(size); } return res; } 

以䞊です



5.


OpenJDK.


 $ hg clone https://hg.openjdk.java.net/jdk/jdk/ jdk-jdk $ cd jdk-jdk $ curl https://shipilev.net/jvm/diy-gc/webrev/jdk-jdk-epsilon.changeset | patch -p1 

OpenJDK :


 $ ./configure --with-debug-level=fastdebug $ make images 

:


 $ build/linux-x86_64-server-fastdebug/images/jdk/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+EpsilonSlidingGC -version openjdk version "13-internal" 2019-09-17 OpenJDK Runtime Environment (build 13-internal+0-adhoc.shade.jdk-jdk-epsilon) OpenJDK 64-Bit Server VM (build 13-internal+0-adhoc.shade.jdk-jdk-epsilon, mixed mode, sharing 


6.


, GC ? :


  1. . . Hotspot , JVM fastdebug , GC.
  2. . , . , ( ) , .
  3. . , , , . - , .

, , :


 $ CONF=linux-x86_64-server-fastdebug make images run-test TEST=gc/epsilon/ Building targets 'images run-test' in configuration 'linux-x86_64-server-fastdebug' Test selection 'gc/epsilon/', will run: * jtreg:test/hotspot/jtreg/gc/epsilon Running test 'jtreg:test/hotspot/jtreg/gc/epsilon' Passed: gc/epsilon/TestAlwaysPretouch.java Passed: gc/epsilon/TestAlignment.java Passed: gc/epsilon/TestElasticTLAB.java Passed: gc/epsilon/TestEpsilonEnabled.java Passed: gc/epsilon/TestHelloWorld.java Passed: gc/epsilon/TestLogTrace.java Passed: gc/epsilon/TestDieDefault.java Passed: gc/epsilon/TestDieWithOnError.java Passed: gc/epsilon/TestMemoryPools.java Passed: gc/epsilon/TestMaxTLAB.java Passed: gc/epsilon/TestPrintHeapSteps.java Passed: gc/epsilon/TestArraycopyCheckcast.java Passed: gc/epsilon/TestClasses.java Passed: gc/epsilon/TestUpdateCountersSteps.java Passed: gc/epsilon/TestDieWithHeapDump.java Passed: gc/epsilon/TestByteArrays.java Passed: gc/epsilon/TestManyThreads.java Passed: gc/epsilon/TestRefArrays.java Passed: gc/epsilon/TestObjects.java Passed: gc/epsilon/TestElasticTLABDecay.java Passed: gc/epsilon/TestSlidingGC.java Test results: passed: 21 TEST SUCCESS 

? fastdebug . ? - .



7.


- spring-petclinic , Apache Bench GC! , , GC .


: -Xlog:gc -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+EpsilonSlidingGC :


:


 Heap: 20480M reserved, 20480M (100.00%) committed, 19497M (95.20%) used GC(2) Step 0: Prologue 2.085ms GC(2) Step 1: Mark 51.005ms GC(2) Step 2: Calculate new locations 71.207ms GC(2) Step 3: Adjust pointers 49.671ms GC(2) Step 4: Move objects 22.839ms GC(2) Step 5: Epilogue 1.008ms GC(2) GC Stats: 70561 (8.63%) reachable from roots, 746676 (91.37%) reachable from heap, 91055 (11.14%) moved, 2237 (0.27%) markwords preserved GC(2) Heap: 20480M reserved, 20480M (100.00%) committed, 37056K (0.18%) used GC(2) Lisp2-style Mark-Compact (Allocation Failure) 20479M->36M(20480M) 197.940ms 

200 ? GC! , . , , : ( — , ). - ( ).


, GC . , -Xlog:gc -XX:+UseSerialGC — , , :


 GC(46) Pause Young (Allocation Failure) 575M->39M(1943M) 2.603ms GC(47) Pause Young (Allocation Failure) 575M->39M(1943M) 2.606ms GC(48) Pause Young (Allocation Failure) 575M->39M(1943M) 2.747ms GC(49) Pause Young (Allocation Failure) 575M->39M(1943M) 2.578ms 

, 2 . , , GC . -Xlog:gc -XX:+UseSerialGC , , :


 GC(3) Pause Full (Allocation Failure) 16385M->34M(18432M) 1969.694ms GC(4) Pause Full (Allocation Failure) 16385M->34M(18432M) 2261.405ms GC(5) Pause Full (Allocation Failure) 16385M->34M(18432M) 2327.577ms GC(6) Pause Full (Allocation Failure) 16385M->34M(18432M) 2328.976ms 

, . .



8. ?


. , GC OpenJDK — , , .


:


  1. . , // . . , , « » , , .

    GC, java.lang.ref.Reference.referent — Java-, , , - . FinalReference , .
    ReferenceProcessor / / .
  2. VM. VM, , , . . , , , - , .


  3. . — , GC, . , , , .

    mark-compact GC Full GC fallbacks Shenandoah ( OpenJDK 8) G1 ( OpenJDK 10, JEP 307: «Parallel Full GC for G1» ).

  4. . , «» , , , - . . , .


  5. . , , , . , «» — «» «» , .


  6. - GC Handbook .




9.


? GC — , , , GC.


, - GC . , , GC (, Serial GC Parallel GC), .


広告の分。 , 5-6 2019, JPoint — Java-. — OpenJDK, GraalVM, Kotlin . .


Source: https://habr.com/ru/post/J443250/


All Articles