<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、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # 垃圾回收 * 如何判定對象為垃圾對象 - 引用計數法 - 可達性分析法 * 回收策略 - 標記-清除算法 - 復制算法 - 標記-整理算法 - 分代回收算法 * 垃圾回收器 - Serial - Parnew - Cms - G1 # 算法 ## 引用計數算法 在對象中添加一個引用計數器,當有地方引用這個對象的時候,引用計數器的值就+1,當引用失效的時候,計數器的值就-1. 打印gc信息 * -verbose:gc * -xx:+PrintGCDetails ## 可達性分析算法 作為GCRoots的對象 * 虛擬機棧 * 方法區的類屬性所引用的對象 * 方法區中常量所引用的對象 * 本地方法棧中引用的對象 ## 標記-清除算法 * 效率問題 * 空間問題,空間碎片 ## 復制算法 * 堆 - 新生代 - Eden伊甸園 - Survivor 存活區 - Tenured Gen - 老年代 將內存分為一大塊Eden和其中一塊Survivor. 當Eden和Survivor中還存活著的對象一次性復制到另一塊Survivor空間上. 最后清理到剛才的Eden和Survivor. 默認Eden和Survivior比例是8:1. 當Survivor空間不夠用的時候需要依賴其他內存(老年代),進行分配擔保 ## 標記-整理算法 標記過程和標記-清除算法一樣. 后續不是直接對可回收對象進行整理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存 ## 分代收集算法 根據對象的存活周期,把堆分為新生代和老年代. 根據不同區域選擇最適當的算法. 在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量的存活,那就用復制算法,只要付出少量存活對象的復制成本就可以完成收集. 老年代中因為對象存活率高,沒有額外空間對他進行分配擔保,那就用標記-清理,標記-整理算法 # HotSpot算法實現 # 相關概念 ## 并行和并發 * **并行(Parallel)**:指多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態。 * **并發(Concurrent)**:指用戶線程與垃圾收集線程同時執行(但不一定是并行的,可能會交替執行),用戶程序在繼續運行。而垃圾收集程序運行在另一個CPU上。 ## 吞吐量(Throughput) 吞吐量就是**CPU用于運行用戶代碼的時間**與**CPU總消耗時間**的比值,即 **吞吐量 = 運行用戶代碼時間 /(運行用戶代碼時間 + 垃圾收集時間)。** 假設虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。 ## Minor GC 和 Full GC * **新生代GC(Minor GC)**:指發生在新生代的垃圾收集動作,因為Java對象大多都具備朝生夕滅的特性,所以Minor GC非常頻繁,一般回收速度也比較快。具體原理見上一篇文章。 * **老年代GC(Major GC / Full GC)**:指發生在老年代的GC,出現了Major GC,經常會伴隨至少一次的Minor GC(但非絕對的,在Parallel Scavenge收集器的收集策略里就有直接進行Major GC的策略選擇過程)。Major GC的速度一般會比Minor GC慢10倍以上。 ## 收集器配合 展示了7種作用于不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。虛擬機所處的區域,則表示它是屬于新生代收集器還是老年代收集器。 Hotspot實現了如此多的收集器,正是因為目前并無完美的收集器出現,只是選擇對具體應用最適合的收集器。 ![](https://img.kancloud.cn/03/6a/036a050ca122c92a64228625e2151dd1_936x866.png) ## YGC和FGC是什么 ??YGC?:對新生代堆進行gc。頻率比較高,因為大部分對象的存活壽命較短,在新生代里被回收。性能耗費較小。 ??FGC?:全堆范圍的gc。默認堆空間使用到達80%(可調整)的時候會觸發fgc。以我們生產環境為例,一般比較少會觸發fgc,有時10天或一周左右會有一次。 ## 什么時候執行YGC和FGC **YGC的時機:** * edn空間不足 **FGC的時機:** * old空間不足; * perm空間不足; * 顯示調用System.gc()?,包括RMI等的定時觸發; * YGC時的悲觀策略; * dump live的內存信息時(jmap –dump:live)。 對YGC的?觸發時機,相當的顯而易見,就是eden空間不足,?這時候就肯定會觸發ygc 對于FGC的觸發時機,?old空間不足,?和perm的空間不足,?調用system.gc()這幾個都比較顯而易見,就是在這種情況下,?一般都會觸發GC。 最復雜的是所謂的悲觀策略,它觸發的機制是在首先會計算之前晉升的平均大小,也就是從新生代,通過ygc變成新生代的平均大小,然后如果舊生代剩余的空間小于晉升大小,那么就會觸發一次FullGC。sdk考慮的策略是,?從平均和長遠的情況來看,下次晉升空間不夠的可能性非常大,?與其等到那時候在fullGC?不如悲觀的認為下次肯定會觸發FullGC,?直接先執行一次FullGC。而且從實際使用過程中來看,?也達到了比較穩定的效果。 # 新生代收集器 ## Serial收集器 **Stop The World** * 單線程垃圾收集器 * 桌面應用 只會使用一個cpu或者一條收集器會完成,中間這段時間會停止其他線程,等待GC完成. 下圖展示了Serial 收集器(老年代采用Serial Old收集器)的運行過程: ![](https://img.kancloud.cn/94/07/94076585e6b1a34db3514d26fe3a6687_1408x674.png) ## ParNew收集器 Serial收集器多線程版本,使用多線程進行GC ParNew收集器的工作過程如下圖(老年代采用Serial Old收集器): ![](https://img.kancloud.cn/0f/d5/0fd50cfba2d9d9c7e63377749dd8ac3b_1342x396.png) **除了Serial收集器外,目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作** 默認開啟的收集線程數與CPU的數量相同,在CPU非常多的情況下可使用**\-XX:ParallerGCThreads**參數設置 ## Parallel Scavenge收集器 是一個**并行**的**多線程新生代**收集器,它也使用**復制算法** CMS等收集器的關注點是盡可能縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標是**達到一個可控制的吞吐量(Throughput)** **停頓時間越短就越適合需要與用戶交互的程序**,良好的響應速度能提升用戶體驗。而**高吞吐量**則可以高效率地利用CPU時間,盡快完成程序的運算任務,主要適合**在后臺運算而不需要太多交互的任務**。 除了會顯而易見地提供可以精確控制吞吐量的參數,還提供了一個參數**\-XX:+UseAdaptiveSizePolicy**,這是一個開關參數,打開參數后,就不需要手工指定新生代的大小(-Xmn)、Eden和Survivor區的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數了,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種方式稱為**GC自適應的調節策略(GC Ergonomics)**。自適應調節策略也是Parallel Scavenge收集器與ParNew收集器的一個重要區別。 控制吞吐量(執行用戶代碼時間 / (執行用戶代碼的時間 + 垃圾回收所占用的時間)) * -XX:MaxGFPauseMillis垃圾收集器停頓時間 * -XX:CGTimeRatio吞吐量大小 - (0, 100)吞吐量大小 - 99默認 # 老年代收集器 ## Serial Old收集器 Serial Old 是 Serial收集器的老年代版本,它同樣是一個**單線程收集器**,使用**“標記-整理”(Mark-Compact)**算法。 此收集器的主要意義也是在于給Client模式下的虛擬機使用。如果在Server模式下,它還有兩大用途: * 在JDK1.5 以及之前版本(Parallel Old誕生以前)中與Parallel Scavenge收集器搭配使用。 * 作為CMS收集器的后備預案,在并發收集發生**Concurrent Mode Failure**時使用 它的工作流程與Serial收集器相同,這里再次給出Serial/Serial Old配合使用的工作流程圖: ![](https://img.kancloud.cn/77/d2/77d221b8c70c1f2bc9918eb095bddeff_663x204.png) ## Parallel Old收集器 Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用**多線程**和**“標記-整理”**算法. **吞吐量優先”收集器**終于有了比較名副其實的應用組合,在**注重吞吐量**以及**CPU資源敏感**的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器。Parallel Old收集器的工作流程與Parallel Scavenge相同,這里給出Parallel Scavenge/Parallel Old收集器配合使用的流程圖: ![](https://img.kancloud.cn/d9/db/d9db0f60efc69829c405b7d10cf2e112_661x188.png) ## CMS收集器 是一種以**獲取最短回收停頓時間**為目標的收集器,它非常符合那些集中在互聯網站或者B/S系統的服務端上的Java應用,這些應用都非常重視服務的響應速度 **并發的標記-清除收集器** * 工作過程 - 初始標記:僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,需要“Stop The World”. - 并發標記:進行**GC Roots Tracing**的過程,在整個過程中耗時最長. - 重新標記:為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發標記的時間短。此階段也需要“Stop The World”. - 并發清理 ![](https://img.kancloud.cn/4f/d1/4fd14b863d3b48da4526c963dc1b9b33_688x191.png) ![](https://img.kancloud.cn/88/fb/88fba9e2043e271a09de02a813b6c617_616x400.png) 如果那塊內存區域給的不夠會出現Concurrent Mode Failure * 優點 - 并發收集 - 低停頓 * 缺點 - 占用大量的cpu資源 - 無法處理浮動垃圾 - 出現Concurrent Mode Failure - 空間碎片: 標記-清除算法 可能出現“Concurrent Mode Failure”失敗而導致另一次Full GC的產生。**由于CMS并發清理階段用戶線程還在運行著,伴隨程序運行自然就還會有新的垃圾不斷產生。**這一部分垃圾出現在標記過程之后,CMS無法再當次收集中處理掉它們,只好留待下一次GC時再清理掉。這一部分垃圾就被稱為**“浮動垃圾”**。也是由于在垃圾收集階段用戶線程還需要運行,那也就還需要預留有足夠的內存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分空間提供并發收集時的程序運作使用。 # G1收集器 步驟 * **初始標記(Initial Marking)**僅僅只是標記一下GC Roots 能直接關聯到的對象,并且修改**TAMS(Nest Top Mark Start)**的值,讓下一階段用戶程序并發運行時,能在正確可以的Region中創建對象,此階段需要**停頓線程**,但耗時很短。 * **并發標記(Concurrent Marking)**從GC Root 開始對堆中對象進行**可達性分析**,找到存活對象,此階段耗時較長,但**可與用戶程序并發執行**。 * **最終標記(Final Marking)**為了修正在并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄在**線程的Remembered Set Logs**里面,最終標記階段需要**把Remembered Set Logs的數據合并到Remembered Set中**,這階段需要**停頓線程**,但是**可并行執行**。 * **篩選回收(Live Data Counting and Evacuation)** 首先對各個Region中的回收價值和成本進行排序,根據用戶所期望的GC 停頓是時間來制定回收計劃。此階段其實也可以做到與用戶程序一起并發執行,但是因為只回收一部分Region,時間是用戶可控制的,而且停頓用戶線程將大幅度提高收集效率。 通過下圖可以比較清楚地看到G1收集器的運作步驟中并發和需要停頓的階段(Safepoint處): ![](https://img.kancloud.cn/d2/c7/d2c7d32ac9d3194b179ce4f0ca6a51fd_642x183.png) **橫跨整個堆內存** 在G1之前的其他收集器進行收集的范圍都是整個新生代或者老生代,而G1不再是這樣。G1在使用時,Java堆的內存布局與其他收集器有很大區別,它**將整個Java堆劃分為多個大小相等的獨立區域(Region)**,雖然還保留新生代和老年代的概念,但**新生代和老年代不再是物理隔離的了,而都是一部分Region(不需要連續)的集合** **有計劃地避免在整個Java堆中進行全區域的垃圾收集** **在后臺維護一個優先列表** **優先回收價值最大的Region** **避免全堆掃描——Remembered Set** 為了避免全堆掃描的發生,虛擬機**為G1中每個Region維護了一個與之對應的Remembered Set**。虛擬機發現程序在對Reference類型的數據進行寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用的對象是否處于不同的Region之中(在分代的例子中就是檢查是否老年代中的對象引用了新生代中的對象),如果是,便通過CardTable**把相關引用信息記錄到被引用對象所屬的Region的Remembered Set之中**。當進行內存回收時,在GC根節點的枚舉范圍中加入Remembered Set即可保證不對全堆掃描也不會有遺漏。 * 優勢 - 并行與并發 - 分代收集 - 空間整合: 整體來看是基于標記-整理,從局部(兩個Region之間)上來看是基于復制算法實現的 - 可預測的停頓 * 步驟 - 初始標記 - 并發標記 - 最終標記 - 篩選回收
                  <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>

                              哎呀哎呀视频在线观看