主頁 >  其他 > 《深入理解Java虛擬機》閱讀——垃圾回識訓制

《深入理解Java虛擬機》閱讀——垃圾回識訓制

2021-02-22 10:09:52 其他

《深入理解Java虛擬機》閱讀——垃圾回識訓制

  • 前言
  • why——為什么需要垃圾回收
  • what——垃圾回收做些什么
  • where——去哪里回收垃圾
  • how——垃圾回收是怎么做的
    • 垃圾是否要回收
      • 參考計數法
      • 可達性分析演算法
      • 方法區判斷是否可回收
    • 垃圾回收的方式
      • 方法論
        • 標記-清除演算法
        • 復制演算法
        • 標記-整理演算法
        • 分代收集演算法
      • 實作(HotSpot)
        • Serial收集器
        • ParNew收集器
        • Parallel Scavenge收集器
        • Serial Old收集器
        • Parallel Old收集器
        • CMS收集器
        • G1收集器

前言

從小老師就告訴我們學習有3個w和1個h,分別是what做什么、where在哪里、why為什么和how怎么做,于是我們今天也從這四個角度出發,跟著《深入理解Java虛擬機》學習一下jvm的垃圾回識訓制,

why——為什么需要垃圾回收

對于我們Java程式員來說,一開始對于記憶體其實是很不敏感的(至少我是這樣的,因為所有的作業都交給jvm來完成了,我們可以通過一個變數來創建陣列,不需要通過malloc函式來動態分配記憶體;在我們使用完這個陣列之后,不需要通過free函式來手動釋放記憶體,所以為了讓我們變得對計算機不了解一些(不是 所以其實是為了讓我們編程更方便一點,jvm幫我們完成了動態分配記憶體和對記憶體進行垃圾回收,
那么出現了另一個why,為什么我們還要學習記憶體動態分配和垃圾回收呢?

答案很簡單:當需要排查各種記憶體溢位、記憶體泄漏問題時,當垃圾收集成為系統達到更高并發量的瓶頸時,我們就需要對這些“自動化”的技術實施必要的監控和調節 ——《深入理解Java虛擬機》

在上一篇關于Java運行時記憶體的博客中,我們在說到堆疊和堆時,發現堆疊和堆在申請不到足夠的所需的記憶體時就會拋出OutOfMemoryError的例外,這也說明jvm并不是萬能的,也會出錯,因為記憶體是物理設備的,不夠就是不夠,就像褲兜里的錢一樣,不夠就是不夠!雖然我們錢不夠的話我們可以走出店門不買了,但是記憶體不夠程式可就崩潰了,可見我們必須要了解底層的記憶體分配和垃圾回收(這里就先說說垃圾回收,

what——垃圾回收做些什么

有這樣三件事情需要我們思考:

  • 哪些記憶體需要回收?
  • 什么時候回收?
  • 如何回收?

我們思考完之后就是要讓垃圾回收要做的事情了:判斷哪些記憶體區域需要回收、判斷什么時候進行回收、用什么樣的方式進行回收,
判斷哪些記憶體需要回收就是where了,判斷什么時候進行回收和用什么樣的方式回收就是how了,

where——去哪里回收垃圾

首先我們明確垃圾是什么,這里所說的垃圾可不是我們使用電腦時將一個檔案拖到回收站那個垃圾桶里的垃圾,這里的垃圾是指我們在運行Java程式時,new出了一大堆物件,而這堆物件在我們不需要使用的時候它就成了垃圾,因為它“占著茅坑不拉屎”,你都沒有用了還占著記憶體干啥對吧,所以我們就需要去回收它們來釋放掉這些記憶體,
那我們現在要追究去哪里回收記憶體,我們肯定要知道jvm占了哪些記憶體,在上篇博客中我們知道了jvm運行時有堆、虛擬機堆疊、本地方法堆疊、方法區、程式計數器這五個區域,虛擬機堆疊和本地方法堆疊以及程式計數器這三個部分都是在編譯期間可以知道,等于說這三個區域的分配和回收是可以確定的,當方法或者執行緒結束時,記憶體也就跟著回收了,所以我們不用管這三個區域,而堆和方法區不一樣,一個介面中的多個實作類需要的記憶體可能不一樣,一個方法中的多個分支需要的記憶體也可能不一樣,所以我們只有在程式處于運行期間才能知道會創建出哪些物件,于是垃圾回收重點關注堆和方法區這兩個區域,

how——垃圾回收是怎么做的

這里的怎么做其實有兩層含義,一方面是人如何實作垃圾回收的,另一方面是垃圾回收是如何回收垃圾的,其實也就是前面說的判斷什么時候進行回收和用什么樣的方式進行回收了,

垃圾是否要回收

進行垃圾回收的第一步就是我們要知曉某物件是否還存活著,也就是它是否還有用,這里我們會學習到兩種方法進行物件死活的判斷,分別是參考計數法和可達性分析演算法,

參考計數法

參考計數法的概念是這樣的:給物件中添加一個參考計數器,每當有一個地方參考這個物件,計數器就加1,當參考失效時就給計數器減1,如果一個物件的計數器的值為0就代表這個物件沒有用了需要回收,
是不是很簡單,事實上這個演算法不僅實作起來簡單,效率還高,但是幾乎當前主流的Java虛擬機都沒用使用這個演算法來進行垃圾的判斷,這是為什么呢?我們思考到如果兩個物件互相參考,那豈不是哪怕沒有其他物件對它們進行參考,它們的計數器的值永遠都是1,永遠不會被回收,這樣就會產生記憶體泄漏了,
于是我們再瞅瞅另一個演算法是怎樣的,

可達性分析演算法

這個演算法的思路是這樣的:通過一系列的稱為“GC Roots”的物件作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為參考鏈(Reference Chain),當一個物件到GC Roots沒有任何參考鏈相連時,則證明此物件是沒用了的,
在這里插入圖片描述
看上圖,根據可達性分析演算法可知,以GC Roots為起始點向下搜索,搜索到的Object 1、Object 2、Object 3、Object 4這四個物件都是仍然存活著的,而Object 5、Object 6、Object 7這三個物件沒有被搜索到,所以它們是被判定可以被回收的,
那么什么物件可以被當作GC Roots呢?
在Java語言中,可作為GC Roots的物件包括下面幾種:

  • 虛擬機堆疊(堆疊幀中的本地變數表)中參考的物件,
  • 方法區中類靜態屬性參考的物件,
  • 方法區中常量參考的物件,
  • 本地方法堆疊中JNI(即一般說的Native方法)參考的物件,

被上述GC Roots物件直接參考間接參考的物件就被認為是可達的,而不可達的物件就被認為是可以被回收的物件,


所以你們注意到沒有,無論是參考計數法還是可達性分析演算法,都與物件之間的參考有關,可見參考是個重點來的,具體內容請看之后的博客,這里只參考一段書中的內容,

在JDK 1.2以前,Java中的參考的定義很傳統:如果reference型別的資料中存盤的數值代表的是另外一塊記憶體的起始地址,就稱這塊記憶體代表著一個參考,這種定義很純粹,但是太過狹隘,一個物件在這種定義下只有被參考或者沒有被參考兩種狀態,對于如何描述一些“食之無味,棄之可惜”的物件就顯得無能為力,我們希望能描述這樣一類物件:當記憶體空間還足夠時,則能保留在記憶體之中;如果記憶體空間在進行垃圾收集后還是非常緊張,則可以拋棄這些物件,
在JDK 1.2之后,Java對參考的概念進行了擴充,將參考分為強參考(Strong Reference)、軟參考(Soft Reference)、弱參考(Weak Reference)、虛參考(Phantom Reference)4種,這4種參考強度依次逐漸減弱, ——《深入理解Java虛擬機》

另外,其實即使在可達性分析種被判定為可回收的物件也不是一定會被回收的,要真正宣布一個物件死亡,至少要經歷兩次標記程序:在物件經過了可達性分析后發現沒有與GC Roots相連接的參考鏈,那么這個物件會被第一次標記并且進行一次篩選,篩選是看這個物件有沒有必要進行finalize()方法,如果該物件沒有重寫finalize()方法或者finalize()方法已經被呼叫過一次了,就說明該物件沒有必要進行finalize()方法了,也就是說它死亡了,
而如果該物件被認為有必要進行finalize()方法,也就是說這個物件重寫了finalize()方法且還沒有呼叫過,那么如果在重寫的finalize()方法里進行了“自救”也就是重新被GC Roots直接或間接參考上,那么在第二次進行標記時這個物件就不會被認為要被回收了,具體程序參考書上的內容:

如果這個物件被判定為有必要執行finalize()方法,那么這個物件將會放置在一個叫做F-Queue的佇列之中,并在稍后由一個由虛擬機自動建立的、低優先級的Finalizer執行緒去執行它,這里所謂的“執行”是指虛擬機會觸發這個方法,但并不承諾會等待它運行結束,這樣做的原因是,如果一個物件在finalize()方法中執行緩慢,或者發生了死回圈(更極端的情況),將很可能會導致F-Queue佇列中其他物件永久處于等待,甚至導致整個記憶體回收系統崩潰,finalize()方法是物件逃脫死亡命運的最后一次機會,稍后GC將對F-Queue中的物件進行第二次小規模的標記,如果物件要在finalize()中成功拯救自己——只要重新與參考鏈上的任何一個物件建立關聯即可,譬如把自己(this關鍵字)賦值給某個類變數或者物件的成員變數,那在第二次標記時它將被移除出“即將回收”的集合;如果物件這時候還沒有逃脫,那基本上它就真的被回收了, ——《深入理解Java虛擬機》

注意:通過在finalize()方法里進行自救或者做些什么善后作業的方式并不推薦,可以使用try-finally的方式或者其他的方式來做會更好,《深入理解Java虛擬機》的作者建議大家忘記有finalize()這個方法,

方法區判斷是否可回收

參考計數法和可達性分析演算法是用來判斷new出來的物件是否可以回收的,物件都是在堆里的,前面也說了垃圾回收關注堆和方法區這兩塊區域,那么接下來我們說說方法區里是如何判斷是否有垃圾要回收的,
方法區在各個Java虛擬機中的實作不盡相同,但是主要回收的還是廢棄常量和無用的類,判斷廢棄常量跟判斷廢棄物件很像,如果沒有參考指向該常量,那么就說明該常量是廢棄常量,而判斷一個類是否是無用的類的條件就會嚴苛許多,類需要同時滿足下面三個條件才能夠算是一個無用的類:

  • 該類所有的實體都已經被回收,也就是說堆里沒有任何該類的實體物件,
  • 加載該類的ClassLoader已經被回收,
  • 該類對應的Class物件沒有在任何地方被參考,無法在任何地方通過反射訪問該類的方法,

即使一個類滿足了上述三個條件,也不一定會被回收,虛擬機提供了一些引數對其進行控制,例如-verbose:class、-XX:+TraceClassLoading等等,

在大量使用反射、動態代理、CGLib等ByteCode框架、動態生成JSP以及OSGi這類頻繁自定義ClassLoader的場景都需要虛擬機具備類卸載的功能,以保證永久代不會溢位,


到這里說完了如何判斷是否可回收,接下來就是說說回收的方式了,

垃圾回收的方式

方法論

標記-清除演算法

標記-清除演算法是最簡單基礎的演算法,后面的演算法都是基于它的,它的主要思路是先標記出所有需要回收的物件,在標記完候對所有被標記的物件進行統一回收,如下圖:
標記-清除演算法
該演算法有兩個不足,分別是效率問題和會產生大量記憶體碎片,
標記和清除的效率都不高,記憶體碎片太多會導致以后在分配較大物件時因為找不到足夠的連續記憶體而不得不提前觸發另一次垃圾回收,

復制演算法

復制演算法的基本思路是:劃分出兩個相同的區域,每次只在其中一個區域里存放物件,在進行一次gc后將依然存活的物件復制到另一個區域,然后將要回收的物件一次性回收掉,如下圖:
復制演算法
很容易發現這種演算法的優缺點,這種演算法雖然避免了出現大量記憶體碎片而且只要移動堆頂指標來分配記憶體就可以,但是將原本的記憶體空間縮小了一半屬實有點虧,那既然縮小一半有點太虧了,那我們可以少縮小一些,

現在的商業虛擬機都采用這種收集演算法來回收新生代,IBM公司的專門研究表明,新生代中的物件98%是“朝生夕死”的,所以并不需要按照1:1的比例來劃分記憶體空間,而是將記憶體分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor, ——《深入理解Java虛擬機》

所以實際上,我們在使用到復制演算法的時候,是在Eden區和一塊Survivor區中分配記憶體,然后在經過一次gc后將幸存物件復制到另一塊Survivor區中,再一次性回收掉要回收的無用物件,HotSpot虛擬機默認Eden區和Survivor區的大小比例為8:1,也就是說如果是100MB的記憶體,Eden區占80MB,兩塊Survivor分別占10MB,每次能使用的記憶體空間是90%,只縮小了10%的記憶體空間,這就比之前損失一半的記憶體空間劃算多了,
但是雖然研究表明每次可能會回收掉98%的物件,但是也可能會出現存活物件大于10%的情況對吧,所以在一塊Survivor區放不下存活物件的時候,就需要放在其他記憶體(老年代)中進行分配擔保,

標記-整理演算法

標記-整理演算法其實是針對老年代物件存活率高的特點對復制演算法進行的改造,它是這樣的:將依然存活的物件向一側移動,然后回收掉端邊界以外的無用物件,如下圖:
標記-整理演算法

分代收集演算法

這個演算法沒有什么好說的,就是將前面三種演算法進行一個結合,直接看書中的內容,

當前商業虛擬機的垃圾收集都采用“分代收集”(Generational Collection)演算法,這種演算法并沒有什么新的思想,只是根據物件存活周期的不同將記憶體劃分為幾塊,一半是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集演算法,在新生代中,每次垃圾收集時都發現有大批物件死去,只有少量存活,那就選用復制演算法,只需要付出少量存活物件的復制成本就可以完成收集,而老年代中因為物件存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清除”或者“標記-整理”演算法來進行回收, ——《深入理解Java虛擬機》

實作(HotSpot)

注:這里有一部分是如何進入到垃圾回收的實作,因為我暫時還沒有搞懂,就先不寫了,等以后搞懂了再來補充,

首先我們要明確一個概念:垃圾回收器通常是組合起來使用的,也就是說有些垃圾回收器負責回收新生代,有些垃圾回收器負責回收老年代,也有的垃圾回收器都可以回收,我們先來看看JDK1.7的HotSpot虛擬機有哪些垃圾回收器(JDK1.8跟1.7是一樣的),
HotSpot虛擬機的垃圾回收器
兩個垃圾回收器之間存在連線的表示它們可以搭配使用,在上面的區域的垃圾回收器表示它們負責新生代的垃圾回收,而在下面的區域的垃圾回收器表示它們負責老年代的垃圾回收,而在中間的表示既可以進行新生代的垃圾回收也可以進行老年代的垃圾回收,
接下來我們就分別學習一下這幾個垃圾回收器的特性、基本原理和使用場景,

雖然我們是在對各個收集器進行比較,但并非為了挑選出一個最好的收集器,因為直到現在為止還沒有最好的收集器出現,所以我們選擇的只是對具體應用最合適的收集器, ——《深入理解Java虛擬機》

Serial收集器

Serial收集器是最基本、發展歷史最悠久的收集器, ——《深入理解Java虛擬機》

在jdk1.3之前,Serial收集器是新生代垃圾回收的唯一選擇,由此可見它的歷史真的十分悠久了,看它的名字serial串行就可以見名知義了,它在執行垃圾回收的時候是單執行緒執行的嘛,但是這里的單執行緒不止是說它使用一個CPU或一條收集執行緒去進行垃圾回收,這里要重點強調它在執行的時候只有它能執行!也就是會產生“Stop The World”,別的作業執行緒都必須停下來等Serial收集器作業完了才能繼續作業,Serial收集器和Serial Old收集器合作運行的運行程序如下圖:
Serial/Serial Old
從圖中可以看出,Serial負責收集新生代的垃圾使用的是復制演算法,Serial Old負責收集老年代的垃圾使用的是標記-整理演算法,這兩個垃圾收集器在運行的時候都需要將用戶正在作業的行程暫停,所以這就是它們串行運行的缺點,因為如果用戶在使用計算機的時候,突然后臺就來了一下垃圾回收,就啥都暫停了,多難受是不,所以后面就出現了很多并行和并發的垃圾回收器,但是它也有它的優點:簡單且高效,

在用戶的桌面應用場景中,分配給虛擬機管理的記憶體一般來說不會很大,收集幾十兆甚至一兩百兆的新生代(僅僅是新生代使用的記憶體,桌面應用基本上不會再大了),停頓時間完全可以控制在幾十毫秒最多一百多毫秒以內,只要不是頻繁發生,這點停頓是可以接收的,所以,Serial收集器對于運行在Client模式下的虛擬機來說是一個很好的選擇, ——《深入理解Java虛擬機》

注:上面提到了并行和并發,,在垃圾回收器的背景關系語境中這兩個詞語與我們平時理解的會有些許的不同,這里解釋一下,并行(Parallel)是指多條垃圾收集執行緒并行作業,但此時用戶執行緒仍然處于等待狀態,并發(Concurrent)是指用戶執行緒與垃圾收集執行緒同時執行(但不一定是并行的,可能會交替執行),用戶程式在繼續運行,


ParNew收集器

ParNew其實是Serial收集器的多執行緒版本,也是上面說的并行垃圾收集器,它與Serial的差別只是Serial是單執行緒而ParNew是多執行緒,其余的比如jvm控制引數(如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold)、收集演算法(復制)、會發生STW、物件分配規則、回收策略等等都是一樣的,所以它和Serial Old垃圾回收器配合使用的運行程序是這樣的:
ParNew/Serial Old
由此圖可看出ParNew垃圾收集器相對Serial收集器并沒有做出多大的創新,而且ParNew也不見得就一定比Serial的效率要高,在單CPU的場景下,Serial的效率肯定是要高于ParNew的,因為ParNew還有執行緒切換帶來的性能消耗,但是如果在多物理CPU或者多邏輯CPU(超執行緒)的情況下ParNew的優勢就體現出來了,ParNew默認開啟的收集執行緒數與CPU的數量相同,可以使用-XX:ParallelGCThreads引數來進行控制,

ParNew是許多運行在Server模式下的虛擬機中首選的新生代收集器,其中有一個與性能無關但很重要的原因是,除了Serial收集器外,目前jdk7剛出來的時候 只有它能與CMS收集器配合作業, ——《深入理解Java虛擬機》

CMS收集器是作業在老年代的垃圾回收器,是JDK1.5的時候推出的,它是第一款真正意義上的并發收集器,CMS之后再學習,另外新生代還有一個垃圾收集器也就是Parallel Scavenge收集器,它是JDK1.4推出的,因為它沒用使用傳統的GC收集器代碼框架,是另外獨立實作的,所以在老年代使用CMS收集器的時候,新生代只能選擇ParNew或者Serial收集器,ParNew收集器也是使用-XX:+UseConcMarkSweepGC選項后的默認新生代收集器,也可以使用-XX:+UseParNewGC選項來強制指定它,


Parallel Scavenge收集器

Parallel Scavenge收集器和ParNew收集器一樣是運行在新生代的、并行的、使用復制演算法的,但是它們之間有一個很大的不同,就是它們的目的或者說關注點不一樣,像ParNew或者CMS這樣的收集器主要關注的是垃圾回收導致用戶執行緒停頓的時間長短,而Parallel Scavenge收集器關注的是吞吐量,它的目標是達到一個可控制的吞吐量(Throughput),什么是吞吐量呢?直白一點,吞吐量 = 用戶執行緒運行時間 / (用戶執行緒運行時間 + 垃圾回識訓費的時間),也就是說如果jvm運行了100分組,而垃圾回識訓費了1分鐘,用戶執行緒運行了99分鐘,那么吞吐量就等于99%,由于與吞吐量關系密切,Parallel Scavenge收集器也經常被稱為“吞吐量優先”收集器,

停頓時間越短就越適合需要與用戶互動的程式,良好的回應速度能提升用戶體驗,而高吞吐量則可以高效率地利用CPU時間,盡快完成程式的運算任務,主要適合在后臺運算而不需要太多互動的任務, ——《深入理解Java虛擬機》

Parallel Scavenge收集器提供兩個引數用來精準的控制吞吐量,分別是-XX:MaxGCPauseMillis和-XX:GCTimeRatio,-XX:MaxGCPauseMillis引數見名知義能看出來這是用來控制垃圾回收停頓時間的,設定這個引數的時候要傳入一個大于0的值,收集器將盡可能的保證垃圾回收產生的停頓時間不超過該值,但是這個值不是越小越好的,這個值設定的越小,那么垃圾回收的次數必然會增多,-XX:GCTimeRatio引數也能看出來是設定垃圾回收時間比例的,事實上它是用來設定允許的垃圾回收的時間占總時間的最大比例,即1 - 吞吐量,但是它不是直接設定的,它需要傳入一個大于0小于100的整數,假設傳入的是9,那么允許的最大GC時間就占總時間的10%(1/(1 + 9) = 0.1),這個引數的默認值是99,也就是說默認允許最大的垃圾回收時間占總時間的1%,
除了上述兩個引數之外,Parallel Scavenge收集器還有一個引數:-XX:+UseAdaptiveSizePolicy,這個引數很厲害的,看到有+就知道這是個開關引數,當作這個引數被打開之后,就不需要我們去手動的設定新生代的大小、Eden區與Survivor區的比例以及晉升老年代物件年齡這些細節引數了,虛擬機能夠根據當前系統的運行情況收集性能監控資訊,去動態的進行調整以提供最合適的停頓時間或者最大的吞吐量,這種調節方式被稱為GC自適應的調節策略,所以對于我們這種菜鳥來說使用這個Parallel Scavenge收集器配合GC自適應再好不過了,把調優任務交給虛擬機,我們只要設定好最大堆和最大停頓時間或者吞吐量就好了,


Serial Old收集器

Serial Old收集器在前面就有提到,它是單執行緒的,使用的是標記-整理演算法,運行的時候會產生STW現象,而且我們在之前的垃圾回收器圖中可以看到,它可以和新生代的Serial收集器、ParNew收集器、Parallel Scavenge收集器配合使用,也可以和老年代的CMS收集器配合使用,按道理它是單執行緒的垃圾回收器,主要意義就是運行在Client模式下了,但是在Server模式下,它可以作為CMS收集器的后備預案,在并發收集發生Concurrent Mode的時候,也就是說新產生的垃圾大于回收的速度了,CMS無法承受壓力了就會啟動Serial Old來進行一次串行的垃圾回收,
另外在Server模式下,在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,

注:需要說明一下,Parallel Scavenge收集器架構中本身有PS MarkSweep收集器來進行老年代收集,并非直接使用了Serial Old收集器,但是這個PS MarkSweep收集器與Serial Old的實作非常接近,所以在官方的許多資料中都是直接以Serial Old代替PS MarkSweep進行講解, ——《深入理解Java虛擬機》

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用的標記-整理演算法,在JDK1.6的時候開始提供,我個人認為Parallel Old收集器是來拯救Parallel Scavenge的,不然Parallel Scavenge在老年代垃圾回收這一塊可太尷尬了,不能和CMS搭配使用,Serial Old收集器又太慢了,白瞎了Parallel Scavenge在新生代的高吞吐量了,所以在Parallel Old收集器出現了才讓“吞吐量優先”收集器有了比較名副其實的應用組合,Parallel Old和Parallel Scavenge搭配使用的運行程序如下圖:
Parallel Old/Parallel Scavenge

CMS收集器

CMS全稱Concurrent Mark Sweep,即并發標記清除,所以從名字中我們能獲取到兩個資訊,CMS是并發執行的,這個之前也說過,還有就是它使用的標記-清除演算法,前面也提到過CMS追求的是最短回收停頓時間,所以在互聯網站或者B/S系統的服務端的場景中,CMS就很符合這類應用的需求,
CMS的作業程序分四個階段:

  • 初始標記(CMS initial mark):標記一下GC Roots能直接關聯到的物件,單執行緒會產生STW,但速度很快,
  • 并發標記(CMS concurrent mark):進行GC Roots Tracing,trace是追溯的意思,就是說完善GC Roots參考鏈,并發執行的不會產生STW,需要比較長的時間,
  • 重新標記(CMS remark):修正并發標記的時候因為用戶程式在繼續運行而產生的變動,多執行緒會產生STW,時間比初始標記長但遠小于并發標記,
  • 并發清除(CMS mark sweep):并發地清除被標記的物件,

因為并發標記和并發清除的時間遠大于初始標記和重新標記的時間,而且初始標記和重新標記的時間非常短,所以總體上來說CMS收集器回收程序是和用戶執行緒一起并發執行的,根據上面四個步驟我們也很容易能夠畫出CMS的運行流程圖:
CMS

CMS的優缺點也十分明顯,優點就是它是并發收集的,停頓時間非常短,缺點就要分三個點細細說了:

  • 面向并發設計的程式都對CPU資源非常敏感,CMS也不例外,在并發階段,CMS雖然不會導致用戶執行緒停頓,但是也會因為占用了一部分CPU資源而導致用戶執行緒變慢,CMS默認啟動的回收執行緒數是 (CPU數量 + 3) / 4,所以說當CPU在4個以上時比如5個,CMS會占用 2 / 5 = 40%的CPU資源,并且會隨著CPU個數的增加而降低占用資源比例,但是如果CPU少于4個,比如2個,那么就會占用 1 / 2 = 50%也就是一半的CPU資源,會嚴重影響用戶執行緒的執行速度,
  • CMS收集無法處理浮動垃圾,這個在上面聊Serial Old收集器的時候就說到了,并且說到了jvm會將CMS作為CMS的后備預案,但是注意是在出現了“Concurrent Mode Failure”之后才會啟動Serial Old收集器的,那么怎樣會出現這樣的失敗呢?其實是因為CMS是并發執行的嘛,在并發的程序中會放入新的物件到老年代,所以CMS必須要預留一部分記憶體來存放可能會新增的物件,在JDK1.5的默認設定中,CMS收集器當老年代使用了68%的空間后就會被激活,也可以使用-XX:CMSInitiatingOccupancyFraction來控制,很明顯,如果這個百分比越高的話,CMS被觸發的次數就越少,在JDK1.6中,CMS收集器的啟動閾值已經提升到92%,但是預留記憶體越少就越容易出現“Concurrent Mode Failure”,所以我覺得這個預留記憶體就需要我們自己去根據實際場景進行衡量了,
  • 標記-清除演算法會產生大量的記憶體碎片,針對這個缺點,CMS提供了一個-XX:+UseCMSCompactAtFullCollection開關引數,是說在CMS撐不住要進行Full GC的時候進行一次記憶體整理,但是這個整理程序是沒有辦法并發進行的,所以會產生停頓時間,另外這個引數默認是開啟的,然后虛擬機設計者還提供了一個引數-XX:CMSFullGCsBeforeCompaction,這個引數是設定CMS在經歷過多少次不整理記憶體的Full GC后來一次整理記憶體的Full GC,默認是0,表示每次進入Full GC都進行記憶體整理,

然后這里補充一下CMS第一個缺點的一個過時的解決方案,當時虛擬機提供了一種名叫增量式并發收集器(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器變種,它就是在并發標記和并發清理的時候讓GC執行緒、用戶執行緒交替運行,盡量減少GC執行緒的獨占資源時間,這樣整個垃圾收集的程序就會更長,但對用戶程式的影響就會顯得少一些,實踐證明,這個i-CMS的效果很一般,所以在JDK1.7的時候它就被宣告為“deprecated”過時的了,

G1收集器

G1(Garbage-First)收集器是當今收集器當時jdk1.7是最新的jdk 技術發展的最前沿成果之一,早在JDK1.7剛剛確立專案目標,Sun公司給出的JDK1.7RoadMap里面,它就被視為JDK1.7中HotSpot虛擬機的一個重要進化特征,從JDK 6u14中開始就有Early Access版本的G1收集器供開發人員實驗、試用,由此開始G1收集器的“Experimental(試驗性的)”狀態持續了數年時間,直至JDK 7u4,Sun公司才認為它達到足夠成熟的商用程度,移除了“Experimental”的標識, ——《深入理解Java虛擬機》

而在JDK1.9的時候,jvm就將G1收集器作為默認的收集器了,
由于G1的內容比較多,而且實作與前面學習的收集器有很大區別,所以我就放在后續博客中詳細聊了,

如有錯誤,歡迎指正!!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/262037.html

標籤:其他

上一篇:計算機網路篇 | 分層的協議架構

下一篇:習題9-4 查找書籍 (20 分)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more