您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Java虛擬機垃圾回收的實例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
-verbose:gc 打印垃圾回收簡單信息參數
-xx:+PringDCDetail 打印垃圾回收的詳細信息
引用計數算法很簡單,它實際上是通過在對象頭中分配一個空間來保存該對象被引用的次數。如果該對象被其它對象引用,則它的引用計數加一,如果刪除對該對象的引用,那么它的引用計數就減一,當該對象的引用計數為0時,那么該對象就會被回收。
采用引用計數的垃圾收集機制中,垃圾收集的開銷被分攤到整個應用程序的運行當中了,而不是在進行垃圾收集時,要掛起整個應用的運行,直到對堆中所有對象的處理都結束。因此,采用引用計數的垃圾收集不屬于嚴格意義上的"Stop-The-World"的垃圾收集機制。
引用計數算法有一個比較大的問題,那就是它不能處理環形數據,即如果有兩個對象相互引用,那么這兩個對象就不能被回收,因為它們的引用計數始終為1。這也就是我們常說的“內存泄漏”問題。
Python中采用的是引用計數機制為主,標記-清除和分代收集兩種機制為輔的策略。
為了引用計數算法中循環引用導致垃圾不會被回收的問題,在Java中采取了 可達性分析法。同樣采用此法的還有C#、Lisp(最早的一門采用動態內存分配的語言)。該方法的基本思想是通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。被判定為不可達的對象要成為可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
GC Roots的對象
虛擬機棧(棧幀中的本地變量表)中引用的對象。(可以理解為:引用棧幀中的本地變量表的所有對象)
方法區中靜態屬性引用的對象(可以理解為:引用方法區該靜態屬性的所有對象)
方法區中常量引用的對象(可以理解為:引用方法區中常量的所有對象)
本地方法棧中(Native方法)引用的對象(可以理解為:引用Native方法的所有對象)
Java虛擬機將堆內存劃分為新生代、老年代和永久代。
新生代(Young)
HotSpot將新生代劃分為三塊,一塊較大的Eden空間和兩塊較小的Survivor空間,默認比例為8:1:1。劃分的目的是因為HotSpot采用復制算法來回收新生代,設置這個比例是為了充分利用內存空間,減少浪費。新生成的對象在Eden區分配(大對象除外,大對象直接進入老年代),當Eden區沒有足夠的空間進行分配時,虛擬機將發起一次Minor GC。
老年代(Old)
在新生代中經歷了多次(具體看虛擬機配置的閥值)GC后仍然存活下來的對象會進入老年代中。老年代中的對象生命周期較長,存活率比較高,在老年代中進行GC的頻率相對而言較低,而且回收的速度也比較慢。
永久代(Permanent)
永久代存儲類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,對這一區域而言,Java虛擬機規范指出可以不進行垃圾收集,一般而言不會進行垃圾回收。
算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收掉所有被標記的對象。
標記-清除算法的主要缺點:
效率問題:標記和清除過程的效率都不高;
空間問題:標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能會導致,碎片過多會導致大對象無法分配到足夠的連續內存,從而不得不提前觸發GC,甚至Stop The World。
為解決效率問題,“復制”收集算法出現了。它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。 這樣使得每次都是對其中的一塊進行內存回收,內存分配時也就不用考慮內存碎片等復雜情況,只要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。 它的主要缺點有兩個:
效率問題:在對象存活率較高時,復制操作次數多,效率降低;
空間問題:內存縮小了一半;需要使用老年代的額外空間做分配擔保。
From Survivor, To Survivor使用的就是復制算法。老年代不使用這種算法。
復制收集算法在對象存活率較高時就要執行較多的復制操作,效率將會變低。更關鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行分配擔保,以應對被使用的內存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。
根據老年代的特點,有人提出了另外一種“標記-整理”(Mark-Compact)算法,標記過程仍然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存。也稱為標記-整理-清除算法。
GC分代的基本假設:絕大部分對象的生命周期都非常短暫,存活時間短。 “分代收集”算法,把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用復制算法,只需要付出少量存活對象的復制成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清理”或“標記-整理”算法來進行回收。
看完上述內容,你們對Java虛擬機垃圾回收的實例分析有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。