日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JVM从入门到精通(六):JVM调优必备理论知识 - 3种垃圾清除算法,常见的垃圾回收器

發布時間:2024/2/28 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM从入门到精通(六):JVM调优必备理论知识 - 3种垃圾清除算法,常见的垃圾回收器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JVM調優是一層窗戶紙,只是看起來很難。學完本節課,讓你:

  • 熟悉 GC 常用算法,熟悉常見垃圾回收器,具有實際 JVM 調優實戰經驗

What is garbage

什么是垃圾?沒有引用指向的對象就是垃圾。

怎么找到垃圾?

引用計數
不能解決循環引用

根可達算法
JNI 是本地方法用到的對象
《JVM虛擬機規范》對于根對象的定義,下圖右側:

清除垃圾的算法

標記清除算法 mark-sweep

算法相對簡單,存活對象比較多的情況下效率比較高。不適合伊甸區,伊甸區的存活對象比較少。

兩遍掃描,效率偏低,容易產生碎片。(第一遍標記,第二遍清除)

拷貝算法 copying

適用于存活對象比較少的情況。只掃描一次,效率高,沒有碎片。
空間浪費,移動復制對象,需要調整對象引用。

標記壓縮算法 mark-compacting

空間連續,沒有碎片,方便對象分配,也不會產生內存減半
需要掃描兩次,需要移動對象,效率偏低

堆內存邏輯分區(針對分代的垃圾回收器)

  • 除了 Epsilon,ZGC,Shenandoah 之外的GC都是邏輯分代模型
  • G1是邏輯分代,物理不分代(物理分代就是內存里確實有這樣一塊空間)
  • 除此之外,不僅邏輯分代,而且物理分代。
// 查看老年代和新生代占用的空間比例 java -XX:+PrintFlagsFinal -version | grep NewRatio


新生代
分為一個伊甸區,兩個survivor幸存區
新生代存活對象少,使用的是拷貝算法

老年代
老年代存活對象多,使用的是標記壓縮算法,或者標記清除算法

一個對象從出生到消亡

與GC的概念有關的一些專業名詞

詳解

C語言struct都可以在棧上分配;
為了對標C,Java中的小對象、無逃逸(就在某段代碼中使用)、支持標量替換(可以用普通的int等屬性代替整個對象)、無需調整這樣的對象分配在棧上。
如果棧上分配不下的話,會把它們分配到 線程本地 TLAB(ThreadLocal Allocation Buffer)
線程本地空間是線程獨有的,避免多線程的爭用,效率較高

package com.mashibing.jvm.c5_gc; //-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB -Xlog:c5_gc* // -代表去掉屬性 // -XX:-DoEscapeAnalysis 去掉逃逸分析 // -XX:-EliminateAllocations 去掉標量替換 // -XX:-UseTLAB 去掉線程專有對象分配 public class TestTLAB {//User u; // 如果在這里寫User u,方法里面 u=new User(), 這個叫就是有逃逸,因為這個對象被外面的給引用了。class User {int id;String name;public User(int id, String name) {this.id = id;this.name = name;}}void alloc(int i) {new User(i, "name " + i); // 逃逸:u=new User()}public static void main(String[] args) {TestTLAB t = new TestTLAB();long start = System.currentTimeMillis();for (int i = 0; i < 1000_0000; i++) t.alloc(i);long end = System.currentTimeMillis();System.out.println(end - start);} }

對象何時進入老年代

回收多少次進入老年代?可以使用參數指定,默認的是如下:
PS 只有4位,所以最大是15,不可能調的更大
CMS 是 6

如果伊甸區+S1區,整體超過了S2的50%,會將年齡最大的超過的部分直接放入老年代。

小總結


動態年齡:(不重要)
https://www.jianshu.com/p/989d3b06a49d

分配擔保:(不重要)
YGC期間 survivor區空間不夠了 空間擔保直接進入老年代
參考:https://cloud.tencent.com/developer/article/1082730

常見的垃圾回收器

常見組合:

  • Serial+Serial Old
  • ParNew+CMS
  • Parallel Scavenge+Parallel Old

只要虛線連在一起的,就能進行組合

  • 常見的垃圾回收器的歷史:JDK誕生 Serial追隨 提高效率,誕生了PS,為了配合CMS,誕生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它開啟了并發回收的過程,但是CMS毛病較多,因此目前任何一個JDK版本默認是CMS
    并發垃圾回收是因為無法忍受STW

  • Serial 年輕代 串行回收

  • PS 年輕代 并行回收

  • ParNew 年輕代 配合CMS的并行回收

  • SerialOld

  • ParallelOld

  • ConcurrentMarkSweep 老年代 并發的, 垃圾回收和應用程序同時運行,降低STW的時間(200ms)
    CMS問題比較多,所以現在沒有一個版本默認是CMS,只能手工指定
    CMS既然是MarkSweep,就一定會有碎片化的問題,碎片到達一定程度,CMS的老年代分配對象分配不下的時候,使用SerialOld 進行老年代回收

    想象一下:
    PS + PO -> 加內存 換垃圾回收器 -> PN + CMS + SerialOld(幾個小時 - 幾天的STW)
    幾十個G的內存,單線程回收 -> G1 + FGC 幾十個G -> 上T內存的服務器 ZGC
    算法:三色標記 + Incremental Update

  • G1(10ms)
    算法:三色標記 + SATB

  • ZGC (1ms) PK C++
    算法:ColoredPointers + LoadBarrier

  • Shenandoah
    算法:ColoredPointers + WriteBarrier

  • Eplison

  • PS 和 PN區別的延伸閱讀:
    https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-3D0BB91E-9BFF-4EBB-B523-14493A860E73

  • 垃圾收集器跟內存大小的關系(粗略估計)

  • Serial 幾十兆
  • PS 上百兆 - 幾個G
  • CMS - 20G
  • G1 - 上百G
  • ZGC - 4T - 16T(JDK13)
  • 1.8默認的垃圾回收:PS + ParallelOld

    常見垃圾回收器組合參數設定:(1.8)

    • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old

      • 小型程序。默認情況下不會是這種選項,HotSpot會根據計算及配置和JDK版本自動選擇收集器
    • -XX:+UseParNewGC = ParNew + SerialOld

      • 這個組合已經很少用(在某些版本中已經廢棄)
      • https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future
    • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old

    • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默認) 【PS + SerialOld】

    • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

    • -XX:+UseG1GC = G1

    • Linux中沒找到默認GC的查看方法,而windows中會打印UseParallelGC

      • java +XX:+PrintCommandLineFlags -version
      • 通過GC的日志來分辨
    • Linux下1.8版本默認的垃圾回收器到底是什么?

      • 1.8.0_181 默認(看不出來)Copy MarkCompact
      • 1.8.0_222 默認 PS + PO

    Serial 垃圾回收器

    使用情況
    Serial + Serial Old 這種組合現在基本不用了。因為以前內存小的時候,這種垃圾回收不會消耗很長時間。但是隨著內存越來越大,才誕生了各種各樣不同的垃圾回收器,來管理越來越大的大內存。

    Serial 是一個 stop-the-world(STW),拷貝算法的,單線程的,工作在年輕代 的垃圾回收器。
    垃圾回收時,工作線程全停止,等著垃圾回收線程進行回收。這時候用戶是得不到任何反饋的。
    (耗時較長的垃圾回收耗費大約幾十秒~1分鐘)

    SafePoint
    safe point 含義是在程序運行到安全點上的時候再STW,而不是立刻停止。
    比如解鎖操作完成之后再停止,然后進行垃圾回收。

    Serial Old

    Parallel Scavenge + Parallel Old(PS+PO,默認的組合)

    回收效率:10G內存的話,PS+PO,回收一次要十幾秒

    Parallel Scavenge
    Parallel Scavenge是多線程的STW垃圾回收器,多個線程共同并行回收
    Parallel Scavenge是并行,不是并發。CMS是并發,回收線程和工作線程同時進行。
    但隨著內存越來越大,線程數越來越多,CPU會將資源耗費在線程切換上,因此線程數不可能被無限增多。

    ParNew + CMS

    CMS:并發垃圾回收

    工作在 老年代,是一個暫停時間短的垃圾回收器,這樣才能保證能給用戶在短時間內做出相應。

    CMS 分為4個階段

    初始標記:需要STW,因為初始的垃圾并不多,因此耗費的時間不長
    并發標記:垃圾回收線程和工作線程同時執行。一邊產生垃圾,一邊標記
    重新標記:對在并發標記的過程中新產生的垃圾進行重新標記,或者原來被標記的垃圾變為不是垃圾。因為新產生的垃圾不多,所以時間也不是很長
    并發清理:清理的過程也會產生新的垃圾“浮動垃圾”,需要等下一次CMS重新運行的時候再次清理

    CMS的問題

  • Memory Fragmentation 內存碎片問題: 因為標記清除會產生碎片化,如果老年代已經沒有地方可以裝了,CMS會請出Serial Old讓它來進行清理Serial Old是單線程的,效率很低

    -XX:+UseCMSCompactAtFullCollection
    -XX:CMSFullGCsBeforeCompaction 默認為0 指的是經過多少次FGC才進行壓縮

  • Floating Garbage 浮動垃圾問題:老年代滿了,浮動垃圾沒有清理完。這時會請出Serial Old讓它來進行清理

    Concurrent Mode Failure
    產生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped
    這個現象在日志里打印出來是PromotionFailed

  • 以上兩個問題的解決方案類似:降低觸發CMS的閾值,保持老年代有足夠的空間。
    –XX:CMSInitiatingOccupancyFraction 92 可以理解為,老年代內存到達92% 的時候,CMS才工作。
    可以降低這個值,讓CMS保持老年代足夠的空間

    可以使用命令查看默認值:

    java -XX:+PrintFlagsFinal -version | grep CMSInitiatingOccupancyFraction

    我看了一下,這個值在1.8和11.0.3版本默認都是-1;
    許多人說的68、92之類的,貌似是根據計算公式得出的,就沒有再去深究,你們可以看一看


    優化環境

  • 有一個50萬PV的資料類網站(從磁盤提取文檔到內存)原服務器32位,1.5G
    的堆,用戶反饋網站比較緩慢,因此公司決定升級,新的服務器為64位,16G
    的堆內存,結果用戶反饋卡頓十分嚴重,反而比以前效率更低了。原因是啥?內存太大了
  • 為什么原網站慢?
    很多用戶瀏覽數據,很多數據load到內存,內存不足,頻繁GC,STW長,響應時間變慢
  • 為什么會更卡頓?
    內存越大,FGC時間越長
  • 咋辦?
    PS -> PN + CMS 或者 G1
  • 系統CPU經常100%,如何調優?(面試高頻)
    CPU100%那么一定有線程在占用系統資源,
  • 找出哪個進程cpu高(top)
  • 該進程中的哪個線程cpu高(top -Hp)
  • 導出該線程的堆棧 (jstack)
  • 查找哪個方法(棧幀)消耗時間 (jstack)
  • 工作線程占比高 | 垃圾回收線程占比高
  • 系統內存飆高,如何查找問題?(面試高頻)
  • 導出堆內存 (jmap)
  • 分析 (jhat jvisualvm mat jprofiler … )
  • 如何監控JVM
  • jstat jvisualvm jprofiler arthas top…
  • 下節課預習:

    • CMS并發標記階段的算法:三色標記

    總結

    以上是生活随笔為你收集整理的JVM从入门到精通(六):JVM调优必备理论知识 - 3种垃圾清除算法,常见的垃圾回收器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。