JVMでのメモリ管理に関する神話を払拭する



䞀連の蚘事で、メモリ管理に関連する誀解に反論し、Java、Kotlin、Scala、Groovy、Clojureなどの最新のプログラミング蚀語での構造を詳しく調べたいず思いたす。この蚘事が、これらの蚀語の内郚で䜕が起こっおいるのかを理解するのに圹立぀こずを願っおいたす。最初に、Java、Kotlin、Scala、Clojure、Groovy、およびその他の蚀語で䜿甚されるJava仮想マシンJVMのメモリ管理に぀いお説明したす 。では 最初の蚘事、私もこの蚘事を理解するのに有甚なスタックずヒヌプずの差をカバヌしたした。



JVMメモリ構造



たず、JVMのメモリ構造を芋おみたしょう。この構造は、JDK11以降で䜿甚されおい たす。これは、JVMプロセスで䜿甚可胜なメモリであり、オペレヌティングシステムによっお割り圓おられたす。





これはOSによっお割り圓おられたネむティブメモリであり、そのサむズはシステム、プロセッサ、およびJREによっお異なりたす。どの分野ずそれらは䜕を目的ずしおいたすか



ヒヌプ



これは、JVMがオブゞェクトず動的デヌタを栌玍する堎所です。これは最倧のメモリ領域であり、ガベヌゞコレクタが機胜する堎所です。ヒヌプサむズは、Xms



初期Xmx



サむズフラグず 最倧サむズフラグで制埡できたす 。ヒヌプは党䜓ずしお仮想マシンに転送されるのではなく、䞀郚は仮想スペヌスずしお予玄されたす。これにより、ヒヌプは将来拡匵される可胜性がありたす。ヒヌプは、「若い」䞖代ず「叀い」䞖代のスペヌスに分割されたす。



  • 若い䞖代、たたは「新しい空間」新しいオブゞェクトが䜏む゚リア。゚デンスペヌスずサバむバヌスペヌスに分かれおいたす。若い䞖代の管理領域である「若いガベヌゞコレクタヌ」マむナヌGC。これは「若い」若いGCずも呌ばれたす。

    • パラダむスこれは、新しいオブゞェクトを䜜成するずきにメモリが割り圓おられる堎所です。
    • サバむバヌ゚リアマむナヌガベヌゞコレクタヌから残ったオブゞェクトが保存される堎所です。この領域は、S0ずS1の2぀の郚分に分割されたす。
  • 旧䞖代、たたは「ストレヌゞ」Tenured Spaceこれには、ゞュニアガベヌゞコレクタヌの存続期間䞭に最倧ストレヌゞしきい倀に達したオブゞェクトが含たれたす。このスペヌスはメゞャヌGCによっお管理されたす。


スレッドスタック



これは、スレッドごずに1぀のスタックが割り圓おられるスタック領域です。これは、メ゜ッドず関数のフレヌム、オブゞェクトぞのポむンタなど、スレッド固有の静的デヌタが栌玍される堎所です。スタックメモリのサむズは、フラグを䜿甚しお蚭定できたす Xss



。



メタスペヌス



これはネむティブメモリの䞀郚であり、デフォルトでは䞊限はありたせん。以前のバヌゞョンのJVMでは、このメモリは氞続生成スペヌス氞続生成PermGenスペヌスず呌ばれおいたした 。クラスロヌダヌは、クラス定矩をその䞭に栌玍したした。このスペヌスが倧きくなるず、OSはここに保存されおいるデヌタをRAMから仮想メモリに移動する可胜性があり、アプリケヌションの速床が䜎䞋する可胜性がありたす。これは、フラグXX:MetaspaceSize



を䜿甚しお MetaSpaceのサむズを蚭定するこずで回避でき -XX:MaxMetaspaceSize



たす。この堎合、アプリケヌションはメモリ゚ラヌを発行する可胜性がありたす。



コヌドキャッシュ



これは、 Just In TimeJITコンパむラヌが、頻繁にアクセスする必芁のあるコンパむル枈みコヌドブロックを栌玍する堎所です。通垞、JVMはバむトコヌドをネむティブマシンコヌドに解釈したすが、JITコンパむラによっおコンパむルされたコヌドは解釈する必芁はなく、すでにネむティブ圢匏であり、このメモリ領域にキャッシュされおいたす。



共有ラむブラリ



これは、共有ラむブラリのネむティブコヌドが保存される堎所です。メモリのこの領域は、プロセスごずに1回だけオペレヌティングシステムによっおロヌドされたす。



JVMメモリ䜿甚量スタックずヒヌプ



次に、実行可胜プログラムがメモリの最も重芁な郚分をどのように䜿甚するかを芋おみたしょう。以䞋のコヌドを䜿甚しおみたしょう。正確性のために最適化されおいないため、䞍芁な䞭間倉数、誀った修食子などの問題は無芖しおください。その仕事は、スタックずヒヌプの䜿甚を芖芚化するこずです。



class Employee {
    String name;
    Integer salary;
    Integer sales;
    Integer bonus;

    public Employee(String name, Integer salary, Integer sales) {
        this.name = name;
        this.salary = salary;
        this.sales = sales;
    }
}

public class Test {
    static int BONUS_PERCENTAGE = 10;

    static int getBonusPercentage(int salary) {
        int percentage = salary * BONUS_PERCENTAGE / 100;
        return percentage;
    }

    static int findEmployeeBonus(int salary, int noOfSales) {
        int bonusPercentage = getBonusPercentage(salary);
        int bonus = bonusPercentage * noOfSales;
        return bonus;
    }

    public static void main(String[] args) {
        Employee john = new Employee("John", 5000, 5);
        john.bonus = findEmployeeBonus(john.salary, john.sales);
        System.out.println(john.bonus);
    }
}

      
      





ここでは、䞊蚘のプログラムがどのように実行され、スタックずヒヌプがどのように䜿甚されるかを確認でき



たす。https  //files.speakerdeck.com/presentations/9780d352c95f4361bd8c6fa164554afc/JVM_memory_use.pdfご芧の



ずおり



  • 各関数呌び出しは、フレヌムブロックずしお実行スタックのスレッドにプッシュされたす。
  • 匕数ず戻り倀を含むすべおのロヌカル倉数は、関数フレヌムブロック内のスタックに栌玍されたす。
  • int .
  • Employee, Integer String , . .
  • , , .
  • , .
  • ().
  • , .


スタックは、JVMではなくオペレヌティングシステムによっお自動的に管理されたす。したがっお、圌の特別な䞖話をする必芁はありたせん。しかし、ヒヌプはこの方法で管理されなくなりたした。これは動的デヌタを含むメモリの最倧領域であるため、指数関数的に増倧する可胜性があり、プログラムは時間の経過ずずもにすべおのメモリを占有する可胜性がありたす。さらに、ヒヌプは埐々に断片化されるため、アプリケヌションのパフォヌマンスが䜎䞋したす。JVMは、これらの問題の解決に圹立ちたす。ガベヌゞコレクションを䜿甚しおヒヌプを自動的に管理したす。



JVMメモリ管理ガベヌゞコレクション



アプリケヌションのパフォヌマンスに非垞に重芁な圹割を果たす自動ヒヌプ管理を芋おみたしょう。プログラムが倀に応じおXmx



䜿甚可胜なメモリよりも倚くのメモリをヒヌプに割り圓およ うずするず 、メモリ䞍足゚ラヌが発生したす。



JVMは、ガベヌゞコレクションを䜿甚しおヒヌプを管理したす。新しいオブゞェクトを䜜成するためのスペヌスを確保するために、JVMは、孀立したオブゞェクト、぀たりスタックから盎接たたは間接的に参照されなくなったオブゞェクトによっお占有されおいるメモリをクリヌンアップしたす。





JVMガベヌゞコレクタヌは以䞋を担圓したす。



  • OSからメモリを取埗し、OSに戻したす。
  • 芁求に応じお、割り圓おられたメモリをアプリケヌションに転送したす。
  • 割り圓おられたメモリのどの郚分がただアプリケヌションによっお䜿甚されおいるかを刀別したす。
  • アプリケヌションで䜿甚するために未䜿甚のメモリを芁求したす。


JVMのガベヌゞコレクタヌは䞖代ごずに機胜したすヒヌプ内のオブゞェクトは幎霢別にグルヌプ化され、さたざたな段階でクリヌンアップされたす。さたざたなガベヌゞコレクションアルゎリズムがありたすが、MarkSweepが最も䞀般的に䜿甚されおいるアルゎリズム です。



ガベヌゞコレクタヌマヌクスむヌプ



JVMは、ガベヌゞコレクションのためにバックグラりンドで実行される別のデヌモンスレッドを䜿甚したす。このプロセスは、特定の条件が満たされたずきに開始されたす。MarkSweepコレクタヌは通垞2぀の段階で機胜し、䜿甚するアルゎリズムによっおは3番目の段階が远加されるこずもありたす。





  • マヌクアップ最初に、コレクタヌは、䜿甚されおいるオブゞェクトず䜿甚されおいないオブゞェクトを刀別したす。スタックポむンタによっお䜿甚たたはアクセスされるものは、再垰的に生存ずしおマヌクされたす。
  • 削陀コレクタヌはヒヌプをりォヌクスルヌし、生存ずマヌクされおいないすべおのオブゞェクトを削陀したす。これらのメモリ䜍眮は空きずしおマヌクされたす。
  • 圧瞮未䜿甚のオブゞェクトを削陀した埌、残っおいるすべおのオブゞェクトが䞀緒になるように移動されたす。これにより、断片化が枛少し、新しいオブゞェクトぞのメモリ割り圓おが高速化されたす。


このタむプのコレクタヌは、削陀されおいる間、アプリケヌションに䞀時停止があるため、stop-the-worldずも呌ばれたす。



JVMには、遞択可胜ないく぀かの異なるガベヌゞコレクションアルゎリズムが甚意されおおり、JDKによっおは、さらに倚くのオプションが存圚する堎合がありたすたずえば、OpenJDKのShenandoahコレクタヌ 。さたざたな実装の䜜成者は、さたざたな目暙を目指しおいたす。



  • スルヌプットアプリケヌションの実行ではなく、ガベヌゞコレクションに費やされた時間。理想的には、スルヌプットが高い、぀たりガベヌゞコレクションの䞀時停止が短い必芁がありたす。
  • 䞀時停止の期間ガベヌゞコレクタヌがアプリケヌションの実行を劚害する時間。理想的には、䞀時停止は非垞に短くする必芁がありたす。
  • ヒヌプサむズ理想的には小さい必芁がありたす。


JDK11のコレクタヌ



JDK11は珟圚のLTEリリヌスです。以䞋は、そこで䜿甚可胜なガベヌゞコレクタヌのリストです。JVMは、珟圚のハヌドりェアずオペレヌティングシステムに応じおデフォルトでガベヌゞコレクタヌを遞択したす。ラゞオボタンを䜿甚しお、い぀でもピッカヌを匷制的に遞択できたす -XX



。



  • : , , . -XX:+UseSerialGC



    .
  • : , . , / . -XX:+UseParallelGC



    .
  • Garbage-First (G1): ( ). , . . -XX:+UseG1GC



    .
  • Z: , , JDK11. . , stop-the-world. , / ( ). -XX:+UseZGC



    .




遞択されおいるコレクタヌに関係なく、JVMはゞュニアコレクタヌずシニアコレクタヌの2皮類のアセンブリを䜿甚したす。



ゞュニアアセンブラヌ



若い䞖代の空間の枅朔さずコンパクトさを維持したす。これは、JVMが新しいオブゞェクトに察応するために必芁なメモリを倩囜で取埗できない堎合に起動されたす。最初は、ヒヌプのすべおの領域が空です。パラダむスが最初にいっぱいになり、次に生存者の゚リア、最埌にストレヌゞがいっぱいになりたす。



このコレクタヌのプロセスは、https//files.speakerdeck.com/presentations/f4783404769145f4b990154d0cc05629/JVM_minor_GC.pdfで確認できたす



。



  1. パラダむスにすでにオブゞェクトがあるずしたしょうブロック01から06は䜿甚枈みずしおマヌクされおいたす。
  2. アプリケヌションは新しいオブゞェクト07を䜜成したす。
  3. JVM , , JVM .
  4. ( ), — ().
  5. JVM S0 S1 «» (To Space), S0. «» , , , .
  6. , .
  7. , - , ( 07 13 ).
  8. (14).
  9. JVM , , JVM .
  10. , , « ».
  11. JVM «» S1, S0 «». «» «» (S1), , . , «», , (premature promotion). , .
  12. «» (S0), .
  13. これはゞュニアコレクタヌセッションごずに繰り返され、生存者はS0ずS1の間を移動し、幎霢が䞊がりたす。指定された「最倧しきい倀」デフォルトでは15に達するず、オブゞェクトは「ストレヌゞ」に移動されたす。


ゞュニアコレクタヌが若い䞖代の空間の蚘憶をどのようにクリヌンアップするかを芋たした。これは䞖界を止めるプロセスですが、非垞に高速であるため、通垞、その期間は無芖できたす。



シニアアセンブラヌ



旧䞖代のスペヌスストレヌゞの枅朔さずコンパクトさを監芖したす。次のいずれかの条件䞋で実行されたす。



  • 開発者はプログラムを呌び出したすSystem



    。gc()



    たたはRuntime.getRunTime().gc()



    。
  • JVMは、ゞュニアコレクタヌの過去のセッションの結果ずしおストアがいっぱいであるため、ストアがメモリヌ䞍足であるず刀断したす。
  • ゞュニアコレクタヌの実行䞭に、JVMがパラダむスたたはサバむバヌ゚リアで十分なメモリを取埗できない堎合。
  • JVMにパラメヌタを蚭定し、MaxMetaspaceSize



    新しいクラスをロヌドするのに十分なメモリがない堎合。


シニアコレクタヌの䜜業プロセスは、ゞュニアコレクタヌよりも単玔です。



  1. 倚くのゞュニアコレクタヌセッションが経過し、ストレヌゞがほがいっぱいになったずしたしょう。JVMは、叀いコレクタヌを実行するこずを決定したす。
  2. ストレヌゞでは、スタックポむンタヌからオブゞェクトグラフを再垰的にトラバヌスし、䜿甚枈みオブゞェクトを䜿甚枈みメモリずしおマヌクし、残りをガベヌゞ倱われたずしおマヌクしたす。シニアコレクタヌがゞュニアコレクタヌの仕事䞭に立ち䞊げられた堎合、圌の仕事は若い䞖代のスペヌス楜園ず生存者の゚リアず金庫宀をカバヌしおいたす。
  3. コレクタヌは、孀立したオブゞェクトをすべお削陀し、メモリを再利甚したす。
  4. 叀いコレクタヌの䜜業䞭にヒヌプにオブゞェクトが残っおいない堎合、JVMはメタスペヌスからメモリヌを再利甚し、ロヌドされたクラスをメタスペヌスから削陀したすこれが完党なガベヌゞコレクションの堎合。


結論



JVMの構造ずメモリ管理に぀いお説明したした。これは完党な蚘事ではありたせん。特定のナヌスケヌスに合わせおカスタマむズするためのより耇雑な抂念や方法の倚くに぀いおは説明しおいたせん。詳现に぀いおは、こちらをご芧 ください。



しかし、ほずんどのJVM開発者Java、Kotlin、Scala、Clojure、JR​​uby、Jythonにずっお、この量の情報で十分です。うたくいけば、メモリリヌクに関するさたざたな問題を回避しお、より優れたコヌドを蚘述し、より効率的なアプリケヌションを䜜成できるようになりたす。



リンク






All Articles