<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                HotSpot JVM的并發標記清理收集器(CMS收集器)的主要目標就是:低應用停頓時間。該目標對于大多數交互式應用很重要,比如web應用。在我們看一下有關JVM的參數之前,讓我們簡要回顧CMS收集器的操作和使用它時可能出現的主要挑戰。 就像吞吐量收集器(參見本系列的第6部分),CMS收集器處理老年代的對象,然而其操作要復雜得多。吞吐量收集器總是暫停應用程序線程,并且可能是相當長的一段時間,然而這能夠使該算法安全地忽略應用程序。相比之下,CMS收集器被設計成在大多數時間能與應用程序線程并行執行,僅僅會有一點(短暫的)停頓時間。GC與應用程序并行的缺點就是,可能會出現各種同步和數據不一致的問題。為了實現安全且正確的并發執行,CMS收集器的GC周期被分為了好幾個連續的階段。 CMS收集器的過程 CMS收集器的GC周期由6個階段組成。其中4個階段(名字以Concurrent開始的)與實際的應用程序是并發執行的,而其他2個階段需要暫停應用程序線程。 初始標記:為了收集應用程序的對象引用需要暫停應用程序線程,該階段完成后,應用程序線程再次啟動。 并發標記:從第一階段收集到的對象引用開始,遍歷所有其他的對象引用。 并發預清理:改變當運行第二階段時,由應用程序線程產生的對象引用,以更新第二階段的結果。 重標記:由于第三階段是并發的,對象引用可能會發生進一步改變。因此,應用程序線程會再一次被暫停以更新這些變化,并且在進行實際的清理之前確保一個正確的對象引用視圖。這一階段十分重要,因為必須避免收集到仍被引用的對象。 并發清理:所有不再被應用的對象將從堆里清除掉。 并發重置:收集器做一些收尾的工作,以便下一次GC周期能有一個干凈的狀態。 一個常見的誤解是,CMS收集器運行是完全與應用程序并發的。我們已經看到,事實并非如此,即使“stop-the-world”階段相對于并發階段的時間很短。 應該指出,盡管CMS收集器為老年代垃圾回收提供了幾乎完全并發的解決方案,然而年輕代仍然通過“stop-the-world”方法來進行收集。對于交互式應用,停頓也是可接受的,背后的原理是年輕帶的垃圾回收時間通常是相當短的。 挑戰 當我們在真實的應用中使用CMS收集器時,我們會面臨兩個主要的挑戰,可能需要進行調優: 堆碎片 對象分配率高 堆碎片是有可能的,不像吞吐量收集器,CMS收集器并沒有任何碎片整理的機制。因此,應用程序有可能出現這樣的情形,即使總的堆大小遠沒有耗盡,但卻不能分配對象——僅僅是因為沒有足夠連續的空間完全容納對象。當這種事發生后,并發算法不會幫上任何忙,因此,萬不得已JVM會觸發Full GC。回想一下,Full GC 將運行吞吐量收集器的算法,從而解決碎片問題——但卻暫停了應用程序線程。因此盡管CMS收集器帶來完全的并發性,但仍然有可能發生長時間的“stop-the-world”的風險。這是“設計”,而不能避免的——我們只能通過調優收集器來它的可能性。想要100%保證避免”stop-the-world”,對于交互式應用是有問題的。 第二個挑戰就是應用的對象分配率高。如果獲取對象實例的頻率高于收集器清除堆里死對象的頻率,并發算法將再次失敗。從某種程度上說,老年代將沒有足夠的可用空間來容納一個從年輕代提升過來的對象。這種情況被稱為“并發模式失敗”,并且JVM會執行堆碎片整理:觸發Full GC。 當這些情形之一出現在實踐中時(經常會出現在生產系統中),經常被證實是老年代有大量不必要的對象。一個可行的辦法就是增加年輕代的堆大小,以防止年輕代短生命的對象提前進入老年代。另一個辦法就似乎利用分析器,快照運行系統的堆轉儲,并且分析過度的對象分配,找出這些對象,最終減少這些對象的申請。 下面我看看大多數與CMS收集器調優相關的JVM標志參數。 -XX:+UseConcMarkSweepGC 該標志首先是激活CMS收集器。默認HotSpot JVM使用的是并行收集器。 -XX:UseParNewGC 當使用CMS收集器時,該標志激活年輕代使用多線程并行執行垃圾回收。這令人很驚訝,我們不能簡單在并行收集器中重用-XX:UserParNewGC標志,因為概念上年輕代用的算法是一樣的。然而,對于CMS收集器,年輕代GC算法和老年代GC算法是不同的,因此年輕代GC有兩種不同的實現,并且是兩個不同的標志。 注意最新的JVM版本,當使用-XX:+UseConcMarkSweepGC時,-XX:UseParNewGC會自動開啟。因此,如果年輕代的并行GC不想開啟,可以通過設置-XX:-UseParNewGC來關掉。 -XX:+CMSConcurrentMTEnabled 當該標志被啟用時,并發的CMS階段將以多線程執行(因此,多個GC線程會與所有的應用程序線程并行工作)。該標志已經默認開啟,如果順序執行更好,這取決于所使用的硬件,多線程執行可以通過-XX:-CMSConcurremntMTEnabled禁用。 -XX:ConcGCThreads 標志-XX:ConcGCThreads=<value>(早期JVM版本也叫-XX:ParallelCMSThreads)定義并發CMS過程運行時的線程數。比如value=4意味著CMS周期的所有階段都以4個線程來執行。盡管更多的線程會加快并發CMS過程,但其也會帶來額外的同步開銷。因此,對于特定的應用程序,應該通過測試來判斷增加CMS線程數是否真的能夠帶來性能的提升。 如果還標志未設置,JVM會根據并行收集器中的-XX:ParallelGCThreads參數的值來計算出默認的并行CMS線程數。該公式是ConcGCThreads = (ParallelGCThreads + 3)/4。因此,對于CMS收集器, -XX:ParallelGCThreads標志不僅影響“stop-the-world”垃圾收集階段,還影響并發階段。 總之,有不少方法可以配置CMS收集器的多線程執行。正是由于這個原因,建議第一次運行CMS收集器時使用其默認設置, 然后如果需要調優再進行測試。只有在生產系統中測量(或類生產測試系統)發現應用程序的暫停時間的目標沒有達到 , 就可以通過這些標志應該進行GC調優。 -XX:CMSInitiatingOccupancyFraction 當堆滿之后,并行收集器便開始進行垃圾收集,例如,當沒有足夠的空間來容納新分配或提升的對象。對于CMS收集器,長時間等待是不可取的,因為在并發垃圾收集期間應用持續在運行(并且分配對象)。因此,為了在應用程序使用完內存之前完成垃圾收集周期,CMS收集器要比并行收集器更先啟動。 因為不同的應用會有不同對象分配模式,JVM會收集實際的對象分配(和釋放)的運行時數據,并且分析這些數據,來決定什么時候啟動一次CMS垃圾收集周期。為了引導這一過程, JVM會在一開始執行CMS周期前作一些線索查找。該線索由 -XX:CMSInitiatingOccupancyFraction=<value>來設置,該值代表老年代堆空間的使用率。比如,value=75意味著第一次CMS垃圾收集會在老年代被占用75%時被觸發。通常CMSInitiatingOccupancyFraction的默認值為68(之前很長時間的經歷來決定的)。 -XX:+UseCMSInitiatingOccupancyOnly 我們用-XX+UseCMSInitiatingOccupancyOnly標志來命令JVM不基于運行時收集的數據來啟動CMS垃圾收集周期。而是,當該標志被開啟時,JVM通過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不僅僅是第一次。然而,請記住大多數情況下,JVM比我們自己能作出更好的垃圾收集決策。因此,只有當我們充足的理由(比如測試)并且對應用程序產生的對象的生命周期有深刻的認知時,才應該使用該標志。 -XX:+CMSClassUnloadingEnabled 相對于并行收集器,CMS收集器默認不會對永久代進行垃圾回收。如果希望對永久代進行垃圾回收,可用設置標志-XX:+CMSClassUnloadingEnabled。在早期JVM版本中,要求設置額外的標志-XX:+CMSPermGenSweepingEnabled。注意,即使沒有設置這個標志,一旦永久代耗盡空間也會嘗試進行垃圾回收,但是收集不會是并行的,而再一次進行Full GC。 -XX:+CMSIncrementalMode 該標志將開啟CMS收集器的增量模式。增量模式經常暫停CMS過程,以便對應用程序線程作出完全的讓步。因此,收集器將花更長的時間完成整個收集周期。因此,只有通過測試后發現正常CMS周期對應用程序線程干擾太大時,才應該使用增量模式。由于現代服務器有足夠的處理器來適應并發的垃圾收集,所以這種情況發生得很少。 -XX:+ExplicitGCInvokesConcurrent and -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses 如今,被廣泛接受的最佳實踐是避免顯式地調用GC(所謂的“系統GC”),即在應用程序中調用system.gc()。然而,這個建議是不管使用的GC算法的,值得一提的是,當使用CMS收集器時,系統GC將是一件很不幸的事,因為它默認會觸發一次Full GC。幸運的是,有一種方式可以改變默認設置。標志-XX:+ExplicitGCInvokesConcurrent命令JVM無論什么時候調用系統GC,都執行CMS GC,而不是Full GC。第二個標志-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses保證當有系統GC調用時,永久代也被包括進CMS垃圾回收的范圍內。因此,通過使用這些標志,我們可以防止出現意料之外的”stop-the-world”的系統GC。 -XX:+DisableExplicitGC 然而在這個問題上…這是一個很好提到- XX:+ DisableExplicitGC標志的機會,該標志將告訴JVM完全忽略系統的GC調用(不管使用的收集器是什么類型)。對于我而言,該標志屬于默認的標志集合中,可以安全地定義在每個JVM上運行,而不需要進一步思考。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看