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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

十个问题弄清JVMGC(二)

發布時間:2024/9/3 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 十个问题弄清JVMGC(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡介: 每個java開發同學不管是日常工作中還是面試里,都會遇到JDK、JVM和GC的問題。本文會從以下10個問題為切入點,帶著大家一起全面了解一下JVM的方方面面。

每個java開發同學不管是日常工作中還是面試里,都會遇到JDK、JVM和GC的問題。本文會從以下10個問題為切入點,帶著大家一起全面了解一下JVM的方方面面。

  • JVM、JRE和JDK的區別和聯系
  • JVM是什么?以及它的主要作用
  • JVM的核心功能有哪些
  • 類加載機制和過程
  • 運行時數據區的邏輯結構
  • JVM的內存模型
  • 如何確定對象是垃圾
  • 垃圾收集的算法有哪些
  • 各種問世的垃圾收集器
  • JVM調優的參數配置
  • 上一篇文章結尾時我們談到,就JVM的設計規范,從使用用途角度JVM的內存大體的分為:線程私有內存區 和 線程共享內存區。

    線程私有內存區在類加載器編譯某個class文件時就確定了執行時需要的“程序計數器”和“虛擬棧幀”等所需的空間,并且會伴隨著當前執行線程的產生而產生,執行線程的消亡而消亡,因此“線程私有內存區”并不需要考慮內存管理和垃圾回收的問題。線程共享內存區在虛擬機啟動時創建,被所有線程共享,是Java虛擬機所管理內存中最應該關注的和最大的一塊。首先我們來一起看一下“線程共享內存區”的內存模型是什么樣的?

    6、JVM的內存模型

    如圖所示,JVM的內存結構分為堆和非堆兩大塊區域。

    • 其中“非堆”就是上篇文章我們提到的方法區或叫元數據區,用來存儲class類信息的。
    • 而“堆”是用來存儲JVM各線程執行期間所創建的實例對象或數組的。堆區分為兩大塊,一個是Old區,一個是Young區。Young區分為兩大塊,一個是Survivor區(S0+S1),一塊是Eden區S0和S1一樣大,也可以叫From和To。

    之所以這樣劃分,設計者的目的無非就是為了內存管理,也就是我們說的垃圾回收。那么什么樣的對象是垃圾?垃圾回收算法有哪些?目前常用的垃圾回收器又有哪些?這篇文章我們一起弄清楚這些問題和知識點。

    7、如何確定一個對象是垃圾?

    要想進行垃圾回收,得先知道什么樣的對象是垃圾。目前確認對象是否為垃圾的算法主要有兩種:引用計數法和可達性分析法。

    • 1、引用計數法:在對象中添加了一個引用計數器,當有地方引用這個對象時,引用計數器的值就加1,當引用失效的時候,引用計數器的值就減1。當引用計數器的值為0時,JVM就開始回收這個對象。

    對于某個對象而言,只要應用程序中持有該對象的引用,就說明該對象不是垃圾,如果一個對象沒有任何指針對其引用,它就是垃圾。這種方法雖然很簡單、高效,但是JVM一般不會選擇這個方法,因為這個方法會出現一個弊端:當對象之間相互指向時,兩個對象的引用計數器的值都會加1,而由于兩個對象時相互指向,所以引用不會失效,這樣JVM就無法回收。

    • 2、可達性分析法:針對引用計數算法的弊端,JVM采用了另一種算法,以一些"GC Roots"的對象作為起始點向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,即可以進行垃圾回收。否則,證明這個對象有用,不是垃圾。


    上圖中的obj7和obj8雖然它們互相引用,但從GC Roots出發這兩個對象不可達,所以會被標記為垃圾。JVM會把以下幾類對象作為GC Roots:

    • (1) 虛擬機棧(棧幀中本地變量表)中引用的對象;
    • (2) 方法區中類靜態屬性引用的對象;
    • (3) 方法區中常量引用的對象;
    • (4) 本地方法棧中JNI(Native方法)引用的對象。

    注:在可達性分析算法中不可達的對象,并不是直接被回收,這時它們處于緩刑狀態,至少需要進行兩次標記才會確定該對象是否被回收:

    第一次標記:如果對象在進行可達性分析后發現沒有與GC Roots相連接的引用鏈,那它將會被第一次標記;

    第二次標記:第一次標記后接著會進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法(該方法可將此對象與GC Roots建立聯系)。在finalize()方法中沒有重新與引用鏈建立關聯關系的,將被進行第二次標記。

    第二次標記成功的對象將真的會被回收,如果對象在finalize()方法中重新與引用鏈建立了關聯關系,那么將會逃離本次回收,繼續存活。

    8、垃圾收集的算法有哪些

    知道了如何JVM確定哪些對象是垃圾后,下面我們來看一下,面對這些垃圾對象,JVM的回收算法都有哪些。

    1、 標記-清除算法(Mark-Sweep)

    • 第一步“標記”,如下圖所示把堆里所有的對象都掃描一遍,找出哪些是垃圾需要回收的對象,并且把它們標記出來。

    • 第二步“清除”,把第一步標記為“UnReference Object”(無引用或不可達)的對象清除掉,釋放內存空間。

    這種算法的缺點主要有兩點:

    (1) 標記和清除兩個過程都比較耗時,效率不高

    (2) 清除后會產生大量不連續的內存碎片空間,碎片空間太多可能會導致當程序后續需要創建較大對象時,無法找到足夠連續的內存空間而不得不再次觸發垃圾回收。

    2、 標記-復制算法(Mark-Copying)

    將內存劃分為兩塊區域,每次使用其中一塊,當其中一塊用滿,觸發垃圾回收的時候,將存活的對象復制到另一塊上去,然后把之前使用的那一塊進行格式化,一次性清除干凈。

    (清除前)

    (清除后)

    “標記-復制”算法的缺點顯而易見,就是內存空間利用率低。

    3、 標記-整理算法(Mark-Compact)

    標記整理算法標記過程仍然與"標記-清除"算法一樣,但是后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存。

    將所有存活的對象向一邊移動,清理掉存活邊界以外的全部內存空間。

    結合這三種算法我們可以看到,

    • “標記-復制”算法的優點是回收效率高,但空間利用率上有一定的浪費。
    • 而“標記-整理”算法由于需要向一側移動等一系列操作,其效率相對低一些,但對內存空間管理上十分優異。
    • 因此,“標記-復制”算法適用于那些生命周期短、回收頻率高的內存對象,
    • 而標記-整理”算法適用于那些生命周期長、回收頻率低,但注重回收一次內存空間得到足夠釋放的場景。

    因此JVM的設計者將JVM的堆內存,分為了兩大塊區域Young區和Old區,Young區存儲的就是那些生命周期短,使用一兩次就不再使用的對象,回收一次基本上該區域十之有八的對象全部被回收清理掉,因此Young區采用的垃圾回收算法也就是“標記-復制”算法。Old區存儲的是那些生命周期長,經過多次回收后仍然存活的對象,就把它們放到Old區中,平時不再去判斷這些對象的可達性,直到Old區不夠用為止,再進行一次統一的回收,釋放出足夠的連續的內存空間。

    9、各種問世的垃圾收集器

    鑒于Young區和Old區需要采用不同的垃圾回收算法,因此在JVM的整個垃圾收集器的演進各個時代里,針對Young區和Old區每個時代都是不同的垃圾收集機制。從JDK1.3開始到目前,JVM垃圾收集器的演進大體分為四個時代:串行時代、并行時代、并發時代和G1時代。

    1、串行時代:Serial(Young區)+ Serial Old(Old區)

    JDK3(1.3)的時候,大概是2000年左右,那個時代基本計算機都是單核一個CPU的,因此垃圾回收最初的設計實現也是基于單核單線程工作的。并且垃圾回收線程的執行相對于正常業務線程執行來說還是STW(stop the world)的,使用一個CPU或者一條收集線程去完成垃圾收集工作,這個線程執行的時候其它線程需要停止。

    串行收集器采用單線程stop-the-world的方式進行收集。當內存不足時,串行GC設置停頓標識,待所有線程都進入安全點(Safepoint)時,應用線程暫停,串行GC開始工作,采用單線程方式回收空間并整理內存。單線程也意味著復雜度更低、占用內存更少,但同時也意味著不能有效利用多核優勢。因此,串行收集器特別適合堆內存不高、單核甚至雙核CPU的場合。

    2、并行時代:Parallel Scavenge(Young區) + Parallel Old(Old區)

    并行收集器是以關注吞吐量為目標的垃圾收集器,也是server模式下的默認收集器配置,對吞吐量的關注主要體現在年輕代Parallel Scavenge收集器上。

    并行收集器與串行收集器工作模式相似,都是stop-the-world方式,只是暫停時并行地進行垃圾收集。年輕代采用復制算法,老年代采用標記-整理,在回收的同時還會對內存進行壓縮。關注吞吐量主要指年輕代的Parallel Scavenge收集器,通過兩個目標參數-XX:MaxGCPauseMills和-XX:GCTimeRatio,調整新生代空間大小,來降低GC觸發的頻率。并行收集器適合對吞吐量要求遠遠高于延遲要求的場景,并且在滿足最差延時的情況下,并行收集器將提供最佳的吞吐量。

    3、 并發時代:CMS(Old區)

    并發標記清除(CMS)是以關注延遲為目標、十分優秀的垃圾回收算法,CMS是針對Old區的垃圾回收實現。

    老年代CMS每個收集周期都要經歷:初始標記、并發標記、重新標記、并發清除。其中,初始標記以STW的方式標記所有的根對象;并發標記則同應用線程一起并行,標記出根對象的可達路徑;在進行垃圾回收前,CMS再以一個STW進行重新標記,標記那些由mutator線程(指引起數據變化的線程,即應用線程)修改而可能錯過的可達對象;最后得到的不可達對象將在并發清除階段進行回收。值得注意的是,初始標記和重新標記都已優化為多線程執行。CMS非常適合堆內存大、CPU核數多的服務器端應用,也是G1出現之前大型應用的首選收集器。

    - 但CMS有以下兩個缺陷:

    • (1)由于它是標記-清除不是標記-整理,因此會產生內存碎片,Old區會隨著時間的推移而終究被耗盡或產生無法分配大對象的情況。最后不得不通過底層的擔保機制(CMS背后有串行的回收作為兜底)進行一次Full GC,并進行內存壓縮。
    • (2)由于標記和清除都是通應用線程并發進行,兩類線程同時執行時會增加堆內存的占用,一旦某一時刻內存不夠用,就會觸發底層擔保機制,又采用串行回收進行一次STW的垃圾回收。

    4、G1時代:Garbage First

    G1收集器時代,Java堆的內存布局與就與其他收集器有很大差別,它將整個Java堆劃分為多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Region(不需要連續)的集合。

    如上圖所示,每一個Region(分區)大小都是一樣的,1~32M之間的數值,但必須是2的指數。設置Region大小通過以下參數:-XX:G1HeapRegionSize=M。
    G1收集器的原理或特點主要有以下三點:

    (1)內存邏輯上仍保留的分代的概念,每一個Region同一時間要么被標記為新生代,要么被標記為老年代,要么處于空閑;

    (2)整體上采用了“標記-整理算法”,不會產生內存碎片

    (3)可預測的停頓,G1整體采用的策略是“篩選回收”,也就是回收前會對各個待回收的Region的回收價值和成本進行排序,根據G1配置所期望的回收時間,選擇排在前面的幾個Region進行回收。

    其實之所以叫G1(Garbage First)就是因為它優先選擇回收垃圾比較多的Region分區。
    整體G1的垃圾回收工作步驟分為:初始標記、并發標記、最終標記和篩選回收。

    5、ZGC:Zero GC

    這篇文章簡單提一下這個最新問世的垃圾收集器,之所以叫“Zero GC”是因為它追求的是更低的GC停頓時間,追求的目標是:支持TB級堆內存(最大4T)、最大GC停頓10ms。JDK11新引入的ZGC收集器,不管是物理上還是邏輯上,ZGC中已經不存在新老年代的概念了會分為一個個page,當進行GC操作時會對page進行壓縮,因此沒有碎片問題。由于其是JDK11和只能在64位的linux上使用,因此目前用得還比較少。

    結語

    以上總體兩篇文章七千字,就是我從JVM的作用、設計框架到JVM內存管理的整體的體系化理解。感謝。

    拓展閱讀:十個問題弄清JVM&GC(二)

    作者:宜信技術學院 譚文濤

    原文鏈接:https://developer.aliyun.com/article/770672?

    版權聲明:本文內容由阿里云實名注冊用戶自發貢獻,版權歸原作者所有,阿里云開發者社區不擁有其著作權,亦不承擔相應法律責任。具體規則請查看《阿里云開發者社區用戶服務協議》和《阿里云開發者社區知識產權保護指引》。如果您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將立刻刪除涉嫌侵權內容。

    總結

    以上是生活随笔為你收集整理的十个问题弄清JVMGC(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

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