您好,登錄后才能下訂單哦!
這篇文章主要介紹“jvm GC怎么掌握”,在日常操作中,相信很多人在jvm GC怎么掌握問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”jvm GC怎么掌握”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在學習GC之前,你首先應該記住一個單詞:“stop-the-world”。Stop-the-world會在任何一種GC算法中發生。Stop-the-world意味著 JVM 因為要執行GC而停止了應用程序的執行。當Stop-the-world發生時,除了GC所需的線程以外,所有線程都處于等待狀態,直到GC任務完成。GC優化很多時候就是指減少Stop-the-world發生的時間。
新生代(Young generation)對象從這個區域消失的過程我們稱之為”minor GC“
老年代(Old generation)對象從老年代中消失的過程,我們稱之為”major GC“(或者”full GC“)
持久代( permanent generation )也被稱為方法區(method area),這個區域也可能發生GC,并且發生在這個區域上的GC事件也會被算為major GC
#新生代的構成
一個伊甸園空間(Eden)
兩個幸存者空間(Survivor) 為了理解GC、首先要了解用于存放所有新建的實例對象的年輕代,年輕代被劃分為三塊:
a)一個Eden space(伊甸園,覺得還是不要直譯的好、怪怪的)
b)兩個Survivor spaces(幸存者空間,依然不再直譯了)
總共三個空間:其中兩個是Survivor spaces,每個空間中事件的執行步驟如下:
注:目的就是在對象被轉移到年老代之前、增加被回收幾率。
從上述步驟可以看出、其中一個Survivor space一定是空的。如果你的兩個Survivor space都有數據、或者都沒有數據、那么很有可能意味著你的程序出了什么問題。
絕大部分新建對象都會被分配到Eden space。
Edenspace執行第一次GC之后,繼續存在的實例對象會被轉移到Survivor space.
Edenspace再執行一次GC之后,繼續存在的實例對象會被轉移到那個已經有實例對象的Survivor space中。
一旦Survivor space滿了之后、繼續存在的對象會被轉移到另一個Survivor space中。然后前面的Survivor space就會被清空。
上述步驟重復到一定次數之后仍然存活的實例對象就會被轉移到年老代中。
在HotSpot VM中、有兩種技術被應用與加快內存分配:
1.Bump-the-pointer
2.TLABs(Thread-Local Allocation Buffers).
Bump-the-pointer(指針)技術追蹤最后一個添加到Eden space的實例對象的位置。此對象會被放置與Eden space頂部。當有再新對象被創建時、只需要檢查最后一個存放在Eden space中的對象的信息就可以知道新建對象是否能夠存放在Eden space。如果可以則會被存放在Edenspace頂部。所以當一個新的對象被創建之后、之后最后一個被存放的對象會被檢測,這種機制大大提升了內存分配效率。然而這種方式卻不適合多線程的環境下、多線程操作時會因為鎖而導致某些線程不能正常執行、這就導致了效率大大降低。
TLABs則是針對這種情況設計的。TLABs允許每個線程把Eden space的一小部分做為自己的操作對象、這樣一來、每個線程就可以使用Bump-top-pointer技術來加快內存分配,從而避免鎖帶來的線程問題。
新建的對象是存放在Eden space中的、經過各種處理依然存活的對象將會被轉移到年老代中。
#老年代GC處理機制
當年老代中存放滿實例對象時會觸發“full GC”執行
單線程執行回收操作,回收期間暫停所有應用線程的執行,client模式下的默認回收器
年輕代:使用的GC算法(bump-top-pointer,TLABs)。設計的選型為復制
年老代的回收分為三個步驟,標記(Mark)、清除(Sweep)、合并(Compact)。
標記階段把所有存活的對象標記出來;
清除階段釋放所有死亡的對象;
合并階段將所有存活對象在堆中從前到后連續排放、并且把堆空間劃分成兩部分、一部分存放對象、另一部分未存放對象(即壓縮部分)。設計的選型為合并,減少內存的碎片
使用多個線程同時進行垃圾回收,多核環境里面可以充分的利用CPU資源,減少回收時間,增加JVM生產率,Server模式下的默認回收器。與串行回收器相同,回收期間暫停所有應用線程的執行
年輕代:使用多個線程回收垃圾,每一個線程的算法與串行回收器相同
老年代:年老代依然是單線程的,與串行回收器相同
年老代分為三個步驟,標記、統計(摘要)、合并。
標記階段,把所有存活的對象劃分為N組(應該與回收線程數相同),每一個線程獨立的負責自己那一組,標記存活對象的位置以及 所在區(Region)的存活率信息,標記為并行的。
統計階段,統計每一個區(Region)的存活率,原則上靠前面的存活率較高,從前到后, 找到值得合并的開始位置(絕大多數對象都存活的區不值得合并),統計階段是串行的(單線程)。
合并階段,依據統計階段的信息,多線程 并行的把存活的對象從一個區(Region)復制到另外一個區(Region)。
這里用到分的思想,把年老代劃分為很多個固定大小的區(region)。–XX:ParallelGCThreads=3還可進一步指定參與并行回收的線程數,與串行回收器相同,回收期間暫停所有應用線程的執行。與并行回收器相比,年老代的回收時間更短,從而減少了暫停時間間隔(Pause time) *
Parallel Old GC在JDK5之后出現。與parallel GC相比,唯一的區別在于針對老年代的GC算法。Parallel Old GC分為三步:標記-匯總-壓縮(mark – summary – compaction)。匯總(summary)步驟與清理(sweep)的不同之處在于,其將依然幸存的對象分發到GC預先處理好的不同區域,算法相對清理來說略微復雜一點。
分為四個步驟,初始標記(Initial Mark)、并發標記(Concurrent Mark)、再次標記(Remark)、以及并發清理(Concurrent Sweep)。特別注意,沒有合并操作,所以會有碎片。
初始化階段: 暫停應用線程,找出所有存活的對象,只有靠近類加載器的存活對象會被標記,因此停頓時間(stop-the-world)比較短暫,回收器使用單線程。所有被幸存對象引用的對象會在并發標記階段被確認是否已經被追蹤和校驗
并發標記階段: 回收器標記操作與應用并發運行,回收器使用單線程標記存活對象。
再次標記:并發標記階段由于應用程序也在運行,這個過程中可能新增或者修改對象。所以再次暫停應用線程,找出所有修改的對象,使用多線程標記。
并發清理:回收器清理操作與應用并發運行,回收器使用單線程清理死亡對象。
一旦采取了這種GC類型,由GC導致的暫停時間會極其短暫。CMS GC也被稱為低延遲GC。它經常被用在那些對于響應時間要求十分苛刻的應用之上。
當然,這種GC類型在擁有stop-the-world時間很短的優點的同時,也有如下缺點:
1)它會比其他GC類型占用更多的內存和CPU
2)默認情況下不支持壓縮步驟
使用CMS GC之前需要對系統做全面的分析。另外為了避免過多的內存碎片而需要執行壓縮任務時,CMS GC會比任何其他GC帶來更多的stop-the-world時間,所以你需要分析和判斷壓縮任務執行的頻率及其耗時情況。
如果你想清晰的理解G1,請先忘記上面介紹的有關新生代和老年代的知識。如上圖所示,每個對象在創建時會分析到一個格子中,后續的GC也是在格子中完成的。每當一個區域分配滿對象后,新創建的對象就會分配到另外一個區域,并開始執行GC。這種GC中不會出現其他GC中的對象在新生代和老生代三區域中移動的現象。G1是為了取代在長期使用中暴露出大量問題且飽受抱怨的CMS GC。
G1最大的改進在于其性能表現,它比以上任何一種GC都更快速。
2、3、4類的年輕代的回收算法(Minor Collection)回收方法相同,所以上面只針對老年代做介紹
方法區:
存放了要加載的類信息、靜態變量、final類型的常量、屬性和方法信息。JVM用永久代(PermanetGeneration)來存放方法區,(在JDK的HotSpot虛擬機中,可以認為方法區就是永久代,但是在其他類型的虛擬機中,沒有永久代的概念,有關信息可以看周志明的書)可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值。
到此,關于“jvm GC怎么掌握”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。