精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java 8 內存管理原理解析及內存故障排查實踐

開發
本文介紹Java8虛擬機的內存區域劃分、內存垃圾回收工作原理解析、虛擬機內存分配配置,以及各垃圾收集器優缺點及場景應用、實踐內存故障場景排查診斷,方便讀者面臨內存故障時有一個明確的思路和方向。

一、背景

Java是一種流行的編程語言,可以在不同的操作系統上運行。它具有跨平臺、面向對象、自動內存管理等特點,Java程序在運行時需要使用內存來存儲數據和程序狀態。

Java的自動內存管理機制是由 JVM 中的垃圾收集器來實現的,垃圾收集器會定期掃描堆內存中的對象,檢測并清除不再使用的對象,以釋放內存資源。

Java的自動內存管理機制帶來了許多好處,首先,它可以避免程序員手動管理內存時的錯誤,例如內存泄漏和懸空指針等問題。其次,它可以提高程序的運行效率,因為程序員不需要頻繁地手動分配和釋放內存,而是可以將更多時間和精力專注于程序的業務邏輯,最后,它可以提高程序的可靠性和穩定性,因為垃圾收集器可以自動檢測和清除不再使用的內存資源,避免內存溢出等問題。

了解和掌握垃圾收集器原理可以幫助提高程序的性能、穩定性和可維護性。

名詞解釋:

響應速度:響應速度指程序或系統對一個請求的響應有多迅速。比如,用戶查詢數據響應時間,對響應速度要求很高的系統,較大的停頓時間是不可接受的。

吞吐量:吞吐量關注在一個特定時間段內應用系統的最大工作量,例如每小時批處理系統能完成的任務數量,在吞吐量方面優化的系統,較長的GC停頓時間也是可以接受的,因為高吞吐量應用更關心的是如何盡可能快地完成整個任務,不考慮快速響應用戶請求。

GC導致的應用暫停時間影響系統響應速度,GC處理線程的CPU使用率影響系統吞吐量。

二、Java 的內存管理

2.1 JVM(Java虛擬機)內存劃分

Java運行時數據區域劃分,Java虛擬機在執行Java程序時,將其所管理的內存劃分為不同的數據區域,每個區域都有特定的用途和創建銷毀的時間。其中,有些區域在虛擬機進程啟動時就存在,而有些區域則是隨著用戶線程的啟動和結束而建立和銷毀。這些數據區域包括程序計數器、虛擬機棧、本地方法棧、堆、方法區等,每個區域都有其自身的特點和作用。了解這些數據區域的使用方式和特點,可以更好地理解Java虛擬機的內存管理機制和運行原理。

JVM的內存區域劃分可分為:1.堆內存空間、2.Java虛擬機棧區域、3.程序計數器、4.本地方法棧、5.元空間區域、6.直接內存。

圖片

圖片

  • 堆內存空間:JVM中占用內存空間最大的是堆,平常對象的創建大部分都是在堆上分配內存的,是垃圾回收的主要目標和方向。
  • 本地方法棧區域:Native Mehod Stack與Java虛擬機棧的作用非常相似,區別是Java虛擬機棧為虛擬機執行Java方法或者為字節碼而服務,本地方法棧是為了Java 虛擬機棧得到Native方法。
  • Java虛擬機棧區域:負責Java的解釋過程、程序的執行過程、入棧和出棧,它是與線程相關的,當啟動一個新的線程時,Java程序就會分配一個Java 虛擬機棧提供運行;Java 虛擬機棧從方法入棧到具體字節碼執行是一個雙層棧結構,可以棧里包含棧。
  • 程序計數器:記錄線程執行位置,線程私有,因為操作系統不停的調度,無法獲取到線程被調度之前的位置,程序計數器提供了這樣一個線程執行位置。
  • 元空間區域:在原來的老的Java 7之前劃分中,永久代用來存放類的元數據信息、靜態變量以及常量池等。在現在Java8后類的元信息存儲在元空間中,靜態變量和常量池等并入堆中,相當于原來的永久代中的數據,被元空間和堆內存給瓜分了。
  • 直接內存:使用了Java 的直接內存的API的內存,例如緩沖ByteBuffer,可以控制虛擬機參數調整大小,而本地內存是使用了native函數操作的內存,是不受JVM管理控制。

堆內存空間

JVM回收的主要目標是堆內存,對象主要的創建分配內存在堆上進行,堆可以想象成一個對象池子,對象不停創建放入池子中,而JVM垃圾回收是不停的回收池子中一些被標記為可回收對象的對象,啟動回收線程進行打掃戰場,當回收對象的速度趕不上程序的創建時,池子就會立馬滿,當滿了之后從而發生溢出,就是常見的OOM。

GC的速度和堆的內存中存活對象的數量有關,與堆內存所有的對象無關,GC的速度和堆內存的大小無關,如一個4GB大小的堆內存和一個16GB的堆內存,只要2個堆內存存活對象都是一樣多的時候,GC速度都是基本差不多。每次垃圾回收也不是必須要把垃圾清理干凈,重要的是保證不把正在使用的對象給標記清除掉。

2.2 堆內存管理

JVM中占用內存空間最大的是堆內存,平常對象的創建大部分都是在堆上分配內存的,是Java垃圾回收的主要目標和方向、是 Java內存管理機制的核心組成部分,它可以自動管理 Java程序的內存分配和釋放,Java垃圾收集器可以自動檢測和回收不再使用的內存,以便重新分配給其他需要內存的程序。這種自動內存管理的機制可以提高程序的運行效率和可靠性,防止因內存泄漏等問題導致程序崩潰或性能下降,Java 垃圾收集器使用了不同的垃圾回收算法和垃圾收集器實現,以適應不同的應用場景和需求。Java垃圾收集器的性能特征和優化技術也是 Java程序員需要了解和掌握的重要知識。

因此,了解 Java垃圾回收的背景、原理和實踐經驗對于編寫高效、可靠的 Java程序非常重要。

2.2.1 對象如何被判斷為可回收

JVM怎么判斷堆內存里面的對象是否可回收的,就是當一個對象沒有任何引用指向它了,它就是可回收對象,判斷的方式有兩種算法,一個是引用計數法,一個是可達性分析法。

可回收對象:

圖片

(1)引用計數法

給對象中添加一個引用計數器,每當有一個地方引用它時,這個計數器值加一,當引用失效斷開時,計數器值就減一,在任何時刻時計數器為0的時候,代表這個對象是可以被回收的,沒有任何引用使用它了。

圖片

引用計數法是有缺點,當對象直接互相依賴引用時,這些對象的計數器都不能為0,都不能被回收。

(2)可達性分析法

它使用tracing(鏈路追蹤)方式尋找存活對象的方法,通過一些列稱為“GC Roots”的對象作為初始點,從這些初始點開始向下查找,直到向下查找沒有任何鏈路時,代表這個對象可以被回收,這種算法是目前Java唯一且默認使用來判定可回收的算法。

圖片

2.2.2 GC Roots的概念和對象類型

  1. Java 虛擬機棧中引用的對象,例如各個線程被調用的方法棧用到的參數、局部變量或者臨時變量等。
  2. 方法區的靜態類屬性引用對象或者說Java類中的引用類型的靜態變量。
  3. 方法區中的常量引用或者運行時常量池中的引用類型變量。
  4. JVM內部的內存數據結構的一些引用、同步的監控對象(被修飾同步鎖)。
  5. JNI中的引用對象。

當然,被GC Roots追溯到的對象不是一定不會被垃圾回收,具體需要看情況,Java 對象與對象引用存在四種引用級別:分別是強引用、軟引用、弱引用、虛引用,默認的對象關系是強引用,只有在和GCRoots沒有關系時才會被回收;軟引用用于維護一些可有可無的對象,當內存足夠時不會被回收;弱引用只要發生了垃圾回收就會被清理;虛引用人如其名形同虛設,任何對象都與它無關。

2.2.3 垃圾對象回收算法

當JVM定位到了那些對象可回收時,這個時候是通過三個算法標記清除,分別是標記清除算法、復制算法、標記壓縮算法。

(1)標記清除算法

首先標記出所有需要回 收的對象,在標記完成后,統一回收掉所有被標記的對象,但是該算法缺點是執行效率低,當大量對象時需要大量標記和清理動作,而且容易產生內存碎片化,當需要一塊連續內存時,會因為碎片化無法分配。

圖片

(2)標記壓縮算法

標記壓縮算法跟清除算法很像,只不過它對內存進行了整理, 讓存活對象都向內存空間的一端移動,然后將邊界的其它對象全部清理,這樣能達到內存碎片化問題,不過它比清除算法多了移步動作。

圖片

(3)復制算法

為了解決標記-清除算法面對大量可回收對象時執行效率低的問題,將存活對象復制到一塊空置的空間里,然后將原來的區域全部清理,缺點是需要額外空間存放存活對象。

圖片

2.2.4 分代垃圾回收模型概念和原理

堆內存分代模型圖


圖片

當JVM進行GC(垃圾回收)時,JVM會發起“Stop the world”,所有的業務線程都進行停止,進入SafePoint狀態,JVM回收垃圾線程開始進行標記和追溯,如何解決這種停止和如何減少STW的時間呢?

目前主流垃圾收集器采用分代垃圾回收方式,大部分對象的聲明周期都比較短,只有少部分的對象才存活的比較長,分代垃圾回收會在邏輯上把堆內存空間分為兩部分,一部分為年輕代,一部分為老年代。

(1)年輕代空間

年輕代主要是存放新生成的對象,一般占用堆空間的三分之一空間,因為會頻繁創建對象,所以年輕代GC頻率是最高的。

分為Eden空間、Survivor1(from)區、Survivor2(to)區,S1和S2總要有一塊空間是空的,為了方便年輕代存活對象來回存放,晉升存活對象年齡。

三個區的默認比例是8:1:1,可以通過配置參數調整比例。

年輕代回收發起Minor GC(YongGC),當Eden內存區域被占滿之后就發起GC,短暫的STW,基于垃圾收集器。

(2)老年代空間

是堆內存中最大的空間, ,里面的對象都是比較穩定或者老頑固,GC頻率不會頻繁執行。

圖片

老年代對象:

  1. 正常提升:由年輕代存活對象年齡到達閾值時,這個對象則會被移動到老年代中。
  2. 分配擔保:如果年輕代中的空間不足時,此時有新的對象需要分配對象空間,需要依賴其它內存進行分配擔保,老年代擔保直接創建。
  3. 大對象:當創建需要大量連續內存空間的對象時,如長字符串或者數組等,大小超過了閾值時,直接在老年代分配。
  4. 動態年齡對象:有的垃圾收集器不需要到達指定年齡大小直接晉升老年代,比如相同年齡的對象的大小總和 > Survivor空間的50%, 年齡大于等于該年齡對象直接移動老年代,無需等待正常提升。

老年代回收發起Major GC / FULL GC,當老年代滿時會觸發MajorGC,通常至少經歷過一次Minor GC,再緊接著進行Major GC, Major GC清理Tenured區,用于回收老年代(CMS才能單獨清理)。

FUll GC:清除整個堆空間,一般來說是針對整個新生代、老生代、元空間的全局范圍的清理。

不管是Major GC還是 Full GC, STW的耗時都是Ygc的十倍以上,所以說對象能在年輕代被回收是最優的。

Full GC觸發條件:

  • 老年代空間不足。
  • 元空間不足擴容導致。
  • 程序代碼執行System.gc時可能會執行。
  • 當程序創建一個大對象時,Eden區域放不下大對象,老年代內存擔保分配,老年代也不足空間時。
  • 年輕代存留對象晉升老年代時,老年代空間不足時。

2.2.5 Java對象內存分配過程

圖片

 對象的分配過程

  1. 編譯器通過逃逸分析優化手段,確定對象是否在棧上分配還是堆上分配。
  2. 如果在堆上分配,則確定是否大對象,如果是則直接進入老年代空間分配, 不然則走3。
  3. 對比tlab, 如果tlab_top + size <= tlab_end, 則在tlab上直接分配,并且增加tlab_top值,如果tlab不足以空間放當前對象,則重新申請一個tlab嘗試放入當前對象,如果還是不行則往下走4。
  4. 分配在Eden空間,當eden空間不足時發生YGC, 幸存者區是否年齡晉升、動態年齡、老年代剩余空間不足發生Full GC 。
  5. 當YGC之后仍然不足當前對象放入,則直接分配老年代。

TLAB作用原理:Java在內存新生代Eden區域開辟了一小塊線程私有區域,這塊區域為TLAB,默認占Eden區域大小的1%, 作用于小對象,因為小對象用完即丟,不存在線程共享,快速消亡GC,JVM優先將小對象分配在TLAB是線程私有的,所以沒有鎖的開銷,效率高,每次只需要線程在自己的緩沖區分配即可,不需要進行鎖同步堆 。

對象除了基本類型的不一定是在堆內存分配,在JVM擁有逃逸分析,能夠分析出一個新的對象所擁有的范圍,從而決定是否要將這個對象分配到堆上,是JVM的默認行為;Java 逃逸分析是一種優化技術,可以通過分析 Java 對象的作用域和生命周期,確定對象的內存分配位置和生命周期,從而減少不必要的內存分配和垃圾回收。可以在棧上分配,可以在棧幀上創建和銷毀,分離對象或標量替換,同步消除。

public class TaoYiFenxi {
 
    Object obj;
 
    public void setObj() {
        obj = new Object();
    }
 
    public Object getObject() {
        Object obj1 = new Object();
        return obj1;
    }
 
 
    public void test1() {
        synchronized (new Object()) {
 
        }
    }
 
}

2.2.6 JVM垃圾收集器特點與原理

(1)Serial垃圾收集器、Serial Old垃圾收集器

圖片

Serial收集器采用復制算法, 作用在年輕代的一款垃圾收集器,串行運行,執行過程中會STW,是使用單個線程進行垃圾回收,響應速度優先。

Serial Old 收集器采用標記整理算法,作用在老年代的一款收集器,串行運行,執行過程中會暫停所有用戶線程,會STW,使用單個線程進行垃圾回收,響應速度優先。

使用場景:

適合內存小幾十兆以內,比較適合簡單的服務或者單CPU服務,避免了線程交互的開銷。

優點:

小堆內存且單核CPU執行效率高。

缺點:

堆內存大,多核CPU不適合,回收時長非常長。

(2)Parallel Scavenge垃圾收集器、Parallel Old垃圾收集器

圖片

Parallel Scavenge垃圾收集器采用了復制算法,作用在年輕代的一款垃圾收集器,是并行的多線程運行,執行過程中會發生STW,關注與程序吞吐量。

Parallel Old垃圾收集器采用標記整理算法,作用,作用在老年代的一款垃圾收集器, 是并行的多線程運行,執行過程中會發生STW,關注與程序吞吐量。

Parallel Scavenge + Parallel Old組合是Java8當中默認使用的一個組合垃圾回收。

所謂的吞吐量是CPU用于運行用戶代碼時間與CPU總消耗時間的比值,也就是說吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集器時間), 錄入程序運行了100分鐘,垃圾收集器花費時間1分鐘,則吞吐量達到了99%。

使用場景:

適用于內存在幾個G之間,適用于后臺計算服務或者不需要太多交互的服務,保證吞吐量的服務。

優點:

可控吞吐量、保證吞吐量,并行收集。

缺點:

回收期間STW,隨著堆內存增大,回收暫停時間增大。

(3)Par New垃圾收集器

Par New垃圾收集器采用了復制算法,作用在年輕代的一款垃圾收集器, 也是并行多線程運行,跟Parallel非常相似,是它的增強版本,或者說是Serial收集器的多線程版本,是搭配CMS垃圾收集器特制的一個收集器。

使用場景:

搭配CMS使用

(4)CMS垃圾收集器

CMS是一款多線程+分段操作的一款垃圾收集器。其最大的優點就是將一次完整的回收過程拆分成多個步驟,并且在執行的某些過程中可以使用戶線程可以繼續運行,分別有初始標記,并發標記,重新標記,并發清理和并發重置。

圖片

CMS是一款多線程+分段操作的一款垃圾收集器。其最大的優點就是將一次完整的回收過程拆分成多個步驟,并且在執行的某些過程中可以使用戶線程可以繼續運行,分別有初始標記,并發標記,重新標記,并發清理和并發重置。

CMS分段

  • 初始標記階段, 這個階段會暫停用戶線程, 掃描所有的根對象,因為根對象比較少,所以一般stw時間都非常短。
  • 并發標記階段,這個階段與用戶線程一起執行,會一直沿著根往下掃描,不停的識別對象是否為垃圾,標記,采用了三色算法, 在對象頭(Mark World)標識了一個顏色屬性,不同的顏色代表不同階段,掃描過程中給與對象一個顏色,記錄掃描位置,防止cpu時間片切換不需要重新掃描。
  • 重新標記階段, 這個階段暫停用戶線程, 修正一些漏標對象,回掃發生引用變化的對象。
  • 并發清理階段, 這個階段與用戶線程一起執行,標記清除已經成為垃圾的對象。

三色標記

  • 黑色:代表了自己已經被掃描完畢,并且自己的引用對象也已經確定完畢。
  • 灰色:代表自己已經被掃描完畢了, 但是自己的引用還沒標記完。
  • 白色:則代表還沒有被掃描過。

標記過程結束后,所有未被標記的對象都是不可達的,可以被回收。

圖片

三色標記算法的問題場景:當業務線程做了對象引用變更,會發生B對象不會被掃描,當成垃圾回收。

public class Demo3 {
 
    public static void main(String[] args) {
        R r = new R();
        r.a = new A();
        B b = new B();
        // GCroot遍歷R, R為黑色, R下面的a引用鏈還未掃完置灰灰色,R.b無引用, 切換時間分片
        r.a.b = b;
        // 業務線程發生了引用改變, 原本r.a.b的引用置為null
        r.a.b = null;
        // GC線程回來繼續上次掃描,發現r.a.b無引用,則認為b對象無任何引用清除
        r.b = b;
        // GC 回收了b, 業務線程無法使用b
    }
}
 
class R {
    A a;
    B b;
}
 
class A {
    B b;
}
 
class B {
}

圖片

當GC線程標記A時,CPU時間片切換,業務線程進行了對象引用改變,這時候時間片回到了GC線程,繼續掃描對象A, 發現A沒有任何引用,則會將A賦值黑色掃描完畢,這樣B則不會被掃描,會標記B是垃圾, 在清理階段將B回收掉,錯誤的回收正常的對象,發生業務異常。

CMS基于這種錯誤標記的解決方案是采取寫屏障 + 增量更新Incremental Update , 在業務線程發生對象變化時,重新將R標識為灰色,重新掃描一遍,Incremental Update 在特殊場景下還是會產生漏標。

圖片


public class Demo3 {
 
    public static void main(String[] args) {
        // Incremental Update還會產生的問題
        R r = new R();
        A a = new A();
        A b = new A();
        r.a1 = a;
        // GC線程切換, r掃完a1, 但是沒有掃完a2, 還是灰色
        r.a2 = b;
        // 業務線程發生引用切換, r置灰灰色(本身灰色)
        r.a1 = b;
        // GC線程繼續掃完a2, R為黑色, b對象又漏了~
    }
}
 
class R {
    A a1;
    A a2;
}
 
class A {
}

當GC 1線程正在標記O, 已經標記完O的屬性 O.1, 準備標記O.2時,業務線程把屬性O,1 = B,這時候將O對象再次標記成灰色, GC 1線程切回,將O.2線程標記完成,這時候認為O已經全部標記完成,O標記為黑色, B對象產生了漏標, CMS針對Incremental Update產生的問題,只能在remark階段,暫停所有線程,將這些發生過引用改變過的,重新掃描一遍。

使用場景:

適用于互聯網或者 B/S服務, 響應速度優先,適合6G左右。

優點:

并發收集, 低停頓,回收過程中最耗時的是并發標記和并發清除,它都能與用戶線程保持一起工作。

缺點:

收集器對CPU的資源非常敏感,會占用用戶線程部分使用,導致程序會變得緩慢,吞吐量下降。

無法處理浮動垃圾,在并發清理階段用戶線程還是在運行,這時候產生的新垃圾無法在這次當中處理,只有等待下次才會清理。

因為CMS使用了Incremental Update,remark階段還是會所有暫停,重新掃描發生引用改變的GC root,效率慢耗時高。

因為收集器是基于標記清除算法實現的,所以在收集器回收結束后,內存會產生碎片化,當碎片化非常嚴重的時候,這時候有大對象進入無法分配內存時會觸發FullGC,特殊場景下會使用Serial收集器,導致停頓不可控。

(5)G1垃圾收集器

G1也是采用三色標記分段式進行回收的算法, 不過它是寫屏障 + STAB快照實現,G1設定的目標是在延遲可控(低暫停)的情況下獲得盡可能高的吞吐量,仍然可以通過并發的方式讓Java 程序繼續運行,G1垃圾收集器在很多方面彌補了CMS的不足,比如CMS使用的是mark-sweep標記清除算法,自然會產生內存碎片(CMS只能在Full GC時,STW 整理內存碎片),然而G1整體來看是基于標記整理算法實現的收集器,但是從局部來看也是基于復制算法實現的,高效的整理剩余內存,而不需要管理內存碎片它。

G1同樣有年輕代和老年代的概念,只不過物理空間劃分已經不存在,邏輯分區還存在,G1會把堆切成若干份,每一份當作一個目標,在部分上目標很容易達成,G1在進行垃圾回收的時候,將會根據最大停頓時間設置值動態選取部分小堆區垃圾回收。

圖片

G1的特點是盡量追求吞吐量,追求響應時間,并發收集,壓縮空閑空間不會延長GC暫停時間,更容易預測GC暫停時間,能充分利用CPU、多核環境下的硬件優勢,使用多個CPU對STW進行控制(200ms以內)靈活的分區回收,優先回收花費時間少的或者垃圾比例高的region新老比例也是動態調整,不需要配置;年齡晉升也是15,但是可以動態年齡,當幸存者region超過了50時,會把年齡最大的放入老年代。

G1動態Y區域設置,G1每個分區都可能是年輕代或者老年代,但是同一時刻只屬于一個代,分代概念還存在,邏輯上分代方便復用以前分代邏輯,在物理上不需要連續,這樣能帶來額外好處,有的分區內垃圾比較多,有的分區比較少,G1會優先回收垃圾比較多的分區,這樣可以花費少量的時間來回收這些分區垃圾,即收集最多垃圾分區;但是新生代回收不適合這種,新生代達到閾值時發生YGC,對整個新生代進行回收或者晉升幸存,新生代也分區是方便動態調整分區大小,在進行垃圾回收時,會將存活對象拷貝到另一個可用分區上,這樣也能避免一定程度的內存碎片化過程,每個分區的大小都是在1M- 32M之間,取決2的冪次方。

Humingous:如果一個對象占用的空間超過了分區容量50%以上,G1收集器就認為這是一個巨型對象。這些巨型對象,默認直接會被分配在年老代,但是如果它是一個短期存在的巨型對象,就會對垃圾收集器造成負面影響;為了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那么G1會尋找連續的H分區來存儲。為了能找到連續的H區,有時候不得不啟動Full GC。

CardTable:記錄每一塊card內存區域是否dirty,如果在發生YGC時,怎么知道那些是存活對象,并且其它代區域有沒有引用這部分對象,于是把內存劃分了很多card區域, 每個區域大小不超過512b,當該card區域里的對象有引用關系,將當前card置為“dirty”, 并且使用卡表(CardTable)來記錄每一塊card是否dirty,在進行GC時,不用遍歷所有的空間, 只需要遍歷卡表中為"dirty"或者說布爾符合條件的card區域進行回掃。

圖片

CSet:Collection SET用于記錄可被回收分區的集合組, G1使用不同算法,動態的計算出那些分區是需要被回收的,將其放到CSet中,在CSet當中存活的數據都會在GC過程中拷貝到另一個可用分區,CSet可以是所有類型分區,它需要額外占用內存,堆空間的1%。

RSet:RememberedSet 每個Region都有一個Rset,是一個記錄了其他Region中的對象到本身Region的引用,它可以使得垃圾收集器不需要掃描整個堆去找到誰的引用了當前分區對象,是G1高效回收的關鍵點,也是三色算法的一個以來點。

圖片

RSet和卡表的區別是什么?

卡表記錄的是堆內存中card有沒有變成"dirty", 但是它本身不知道dirty里面哪些是引用了的對象,它是一個大維度的一個記錄,RSet是記錄自身Region中對象引用了其它Region中的那些對象,詳細的記錄對方引用對象信息,G1使用了兩者的結合,實現了增量式的垃圾回收,并優化跨區引用的最終處理。

SATB算法:是一種基于快照的算法,它可以避免在垃圾回收時出現對象漏標或者重復標記的問題,從而提高垃圾回收的準確性和效率,在垃圾回收開始時,對堆中的對象引用進行快照,然后在并發標記階段中記錄下所有被修改過對象引用,保存到satb_mark_queue中,最后在重新標記階段重新掃描這些對象,標記所有被修改的對象,保證了準確性和效率。

SATB算法在remark階段不需要暫停遍歷整個堆對象,只需要掃描“satb_mark_queue”隊列中的記錄,避免了這個階段長耗時,而cms的增量算法在這個階段是需要重新掃描GC Roots標記整個堆對象,導致了不可控時間暫停,總的來說G1是通過回收領域應用并行化策略,將原來的幾塊大內存塊回收問題,演變成了N個小內存塊回收,使得回收效率可以高度并行化,停頓時間可控,可以與用戶線程并發執行,將一塊內存分而治之。

圖片

G1默認當分區內存占用閾值達到總內存的45%,會發生Mixed gc(混和GC),YoungGC + 并發回收Mixed GC過程:初始標記(stw)、并發標記、最終標記(重新標記stw)、篩選回收(stw并行)。

使用場景:

響應速度優先,較高的吞吐量,面向服務端,使用內存6G以上。

優點:

并行與并發收集,分代分區收集,優先垃圾收集,空間整合,可控或者可預測停頓時間。

缺點:

收集中產生內存,G1的每個region都需要有一份記憶集和卡表記錄跨代指針,這導致記憶集可能占用堆空間10-20%甚至更多空間。

執行過程中額外負載開銷加大,寫屏障進行維護卡表操作外,還需要原始快照能夠減少并發標記和重新標記階段的消耗,避免最終標記階段停頓過長,運行過程中會產生由跟蹤引用變化帶來的額外開銷負擔,比CMS增量算法消耗更多,CMS的寫屏障實現直接是同步操作, 而G1是把寫屏障和寫后屏障中要做的事情放到隊列里異步處理。

G1對于Full GC是沒有處理流程, 一旦發生Full GC G1的回收執行的是單線程的Serial回收器進行回收。

2.2.7 垃圾收集器配置使用

機器配置:64位 4C8G

Java 程序使用CMS收集器進行內存垃圾回收初始內存劃分情況:

-Xms4096M -Xmx4096M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/{runuser}/logs/other -XX:+UseConcMarkSweepGC

圖片

CMS 跟 parNew占比情況, 默認下 ParNew占用整個堆的空間為:機器位數 * CPU核數 * 13 /10 , 當前機器配置計算得出 64 * 4 * 13 / 10 = 332M , 與圖上數值差別不大。

Java程序使用G1收集器進行內存垃圾回收初始內存劃分情況:

-Xms4096M -Xmx4096M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/{runuser}/logs/other -XX:+UseG1GC

圖片

G1 新老年代的占比是動態調整, 隨著運行時根據實際情況劃分空間。

Java8默認ParallerGC收集器初始內存劃分情況:

圖片

parallel GC回收器默認堆old區與young區內存大小比例 2:1, 圖上數值差別不大。

三、內存診斷實踐

3.1 內存快照生成

當發生線上應用告警,告警相關內存故障問題時, 應當如何進行故障排查呢?首先應用在發生內存溢出無法執行時,應DUMP當前內存快照,需要在Java程序執行啟動命令時添加上:

-XX:+HeapDumpOnOutOfMemoryError 

-XX:HeapDumpPath=${filePath} 參數

 當發生時自動生成一份當前內存快照,方便與開發人員使用快照文件進行問題診斷分析。

在Java應用運行時,想手動生成內存快照,可以使用JDK自帶幾個問題排查工具,可以使用jmap工具生成指定PID內存快照,不過需要耗費較長的一個時間,會暫停應用程序執行,使用jcmd工具可以快速的DUMP內存快照,因為在堆轉儲存文件過程中,jcmd可以利用虛擬機中的一些優化技術,例如分代堆、增量式垃圾回收等技術,相比傳統的jmap效率高很多,一般來說在DUMP內存前會進行一次

Full FC,可以指定屏蔽這次Full GC,保留當前所有內存中的對象。

除了自帶的內存診斷工具, 也可以使用Arthas診斷工具,提供了多個命令來幫助診斷內存問題,例如 dashboard(當前Java程序內存實時數據面板)、JVM(查看當前JVM信息,包括使用的gc收集器、內存分區分布情況等信息)、heapdump(當前內存快照類似jmap命令的heap dump)、memory(當前內存分區及占用情況)、monitor(監控模式,可監控內存及查看對象占用情況)profiler(火焰圖可以輸出多種火焰圖,內存分區占用火焰圖)等相關內存命令。這些命令可以幫助獲取應用程序的內存快照、堆內存使用情況等信息,能快速定位內存問題。

引用:Arthas 命令列表

3.2 dump內存快照分析

(1)jhat 是 Java 開發工具包自帶的一款堆內存分析工具,它可以幫助解決 Java 應用程序的內存問題。Jhat 可以讀取 Java 應用程序生成的堆轉儲文件,并以 HTML 格式展示內存中的對象信息和引用關系,支持 OQL 查詢和靈活的過濾和排序功能。

用例  jhat E:\diydump\Java_pid2680.hprof

圖片

  • All classes including platform:列舉應用程序中所有類的信息,并快速定位內存問題。
  • Show all members of the rootset:顯示堆內存中所有根對象的信息,包括系統對象、靜態對象、本地對象等。
  • Show instance counts for all classes (including platform):顯示所有類的實例數量。
  • Show heap histogram:顯示程序堆內存的直方圖,可以知道每個類的實例數量和占用內存大小等信息,快速知道內存泄漏原因。

(2)jvisualvm也是Java 開發工具包里自帶的一款圖形化工具,可以用于監控和診斷Java應用程序的性能問題。使用它可以實時查看Java 應用程序的內存使用情況、CPU使用情況、線程情況等,并可以進行內存分析、CPU分析、線程分析等內容。

以Java_pid2680.hprof為例,進行內存分析內存泄漏原因:

圖片

(3)MAT 是基于Eclipse的內存分析工具,是一個快速、功能豐富的Java內存分析工具,能夠快速的分析出dump文件中各項結果,快速給出內存泄漏原因報告。

還是以Java_pid2680.hprof文件進行分析,比原生的jhat方便很多,功能也比原生的更加豐富:

圖片

MAT的一些常用功能點介紹(如圖所示):

  • Overview 標簽內容有比較多塊內容,其中details末塊介紹總共使用內存大小,類的數量,實例的數量,類的加載器,以及實例的內存直方圖;

  • Biggest Objects by Retained Size模塊,使用了餅狀圖列出了當前內存中占用最大的幾個對象,按照百分比劃分,點擊不同的餅狀塊能夠看到具體對象及其對象屬性等信息;
  • actions模塊,這里擁有不同的分析功能,Histogram生成視圖列出每個類所對應的對象個數以及占用內存大小,Dominator Tree生成視圖尋找出大對象,每個實例對象的內存占比比重;
  • Reports模塊是生成報告,其中Leak Suspects可以自動分析內存泄漏主要原因報告,可以通過報告準確定位泄漏原因或者可能造成泄漏的原因,并且可以定位到具體累積實例,線程stack等信息。

例子中:leak Suspects報告給出“0xfe3be480” 非常多內存, Gc root Thread 所引用,在發生gc時,不是可回收對象,無法回收內存,導致內存溢出。

圖片

四、總結

本文介紹了Java程序中的內存模型,內存模型劃分多份內存區域,不同區域的作用介紹及不同區域的線程之間的內存共享范圍,可以幫助開發人員更加理解Java 中內存管理的機制和原理。

堆是內存模型中最大的一塊內存區域,以堆的空間劃分詳細的介紹了內存分代,部分垃圾收集器即是物理分代和邏輯分代,G1收集器則物理不分代邏輯保留了以前分代,講述了不同收集器的原理實現和優缺點,可以根據項目的業務屬性,機器配置等因素選擇最優的收集器,幫助程序使用最優的收集器可以使得程序的吞吐量和響應速度達到最佳狀態。還講述了不同的參數調優收集器,并且當發生了程序內存溢出崩潰,如何進行內存分析,介紹不同工具的使用,快速定位內存溢出的罪魁禍首,從而在代碼層面上根本解決這類問題。


責任編輯:龐桂玉 來源: vivo互聯網技術
相關推薦

2024-12-04 16:44:51

2012-06-29 13:54:11

Java內存原型

2025-09-08 07:14:25

2023-11-10 07:23:57

Kubernetes集群網絡

2019-02-20 09:29:44

Java內存郵件

2023-09-19 22:47:39

Java內存

2013-03-25 09:19:10

Linux服務器故障排查

2023-10-18 13:31:00

Linux內存

2013-03-26 09:21:40

Linux服務器故障排查

2017-02-09 21:24:22

iOS內存管理

2018-11-20 09:37:19

Java內存模型

2025-09-28 01:00:00

2018-11-26 08:49:42

CPU排查負載

2021-06-28 06:45:06

內存溢出內存泄露JavaScript

2022-03-09 23:02:30

Java編程處理模型

2020-11-04 15:35:13

Golang內存程序員

2009-07-28 09:54:23

.NET內存管理

2023-11-16 09:01:37

Hadoop數據庫

2018-08-10 15:00:42

服務器內存排查

2011-07-21 09:32:07

Objective-C 內存 Autoreleas
點贊
收藏

51CTO技術棧公眾號

免费在线你懂的| 清纯粉嫩极品夜夜嗨av| 性欧美hd调教| 国产精品久久久久aaaa| 99热99热| 成年人晚上看的视频| 91av精品| 精品亚洲国产视频| 伊人成人222| yellow在线观看网址| 亚洲国产成人一区二区三区| 99伊人久久| 成人黄色三级视频| 在线精品亚洲| 久久九九免费视频| 一色道久久88加勒比一| 无人区乱码一区二区三区| 色噜噜狠狠成人中文综合| 最新av网址在线观看| 精品电影在线| 成人av电影在线网| 国产一区二区在线免费视频| 亚洲免费在线观看av| 五月激情久久久| 日韩精品中文字幕视频在线| 精品国产乱码久久久久久1区二区| 亚洲v.com| 一区二区在线免费观看| 亚洲精品一区二| 日韩大胆视频| 成人黄色大片在线观看| 91精品视频在线看| 中文字幕一区二区免费| 久久激情久久| 91成人免费观看网站| 激情五月婷婷小说| 中文精品久久| 久久精品99久久香蕉国产色戒 | 手机av在线播放| 欧美精品一区二区久久| 亚洲成av人影院在线观看| 91 在线视频观看| 激情亚洲影院在线观看| 欧美日韩免费观看中文| 欧日韩免费视频| 牛牛在线精品视频| 亚洲乱码国产乱码精品精的特点 | 午夜不卡在线视频| 奇米777四色影视在线看| 日本视频在线| √…a在线天堂一区| 亚洲午夜精品久久| 瑟瑟视频在线| 18成人在线观看| 一区二区三区久久网| 永久免费在线观看视频| 国产精品美女久久久久aⅴ| 少妇精品久久久久久久久久| h视频在线免费| 国产精品欧美精品| 一本久久a久久精品vr综合| 日本在线免费看| 国产精品超碰97尤物18| 干日本少妇视频| a视频在线观看免费| 一区二区视频免费在线观看| www.xxx麻豆| 日韩深夜视频| 在线亚洲一区观看| 亚洲国产美女搞黄色| 北条麻妃高清一区| 欧美 日韩 国产 成人 在线| www.欧美色图| 日本欧美色综合网站免费| 在线免费观看黄色| 亚洲欧美日韩在线| 国产精品69久久久| 亚洲欧美韩国| 欧美日韩一级二级三级| 成人高清在线观看视频| 国产精品任我爽爆在线播放| 精品亚洲一区二区三区| 国产7777777| 在线看片不卡| 78m国产成人精品视频| 国产精品自拍第一页| 精品综合免费视频观看| 超碰97人人人人人蜜桃| 视频午夜在线| 中文字幕一区二区不卡| bt天堂新版中文在线地址| 在线看片福利| 欧美一区二区三区小说| 手机在线看片日韩| 99精品在线免费在线观看| 欧美日韩xxx| 亚洲一区二区福利视频| 在线免费日韩av| 91亚洲国产| 亚洲91精品在线| 中文字幕永久免费视频| 成人污污视频在线观看| 日本一区二区三区www| av电影免费在线观看| 欧美日韩在线视频一区| 亚洲一级片免费观看| 亚洲婷婷伊人| 欧美黑人极品猛少妇色xxxxx | 在线欧美视频| 国产精品一区二区久久久| 欧美熟妇交换久久久久久分类| 国产欧美日韩三级| 成人性免费视频| 日本欧美在线| 亚洲一区二区久久久| 久久久国产精品黄毛片| 久久电影网站中文字幕| 免费观看成人在线| hd国产人妖ts另类视频| 在线成人免费视频| 免费看裸体网站| 一区二区三区国产盗摄| 亚洲影院高清在线| 91精品专区| 色诱视频网站一区| 欧美精品videossex88| 波多野结衣作品集| 99re8这里有精品热视频8在线| 色综合影院在线| 无码人妻精品一区二| 91丨porny丨首页| 欧美乱大交xxxxx潮喷l头像| 日韩精品一区二区三区免费视频| 日韩在线免费观看视频| 国产精品尤物视频| 久久久精品欧美丰满| aa在线免费观看| 精品综合久久88少妇激情| 欧美麻豆久久久久久中文| 91麻豆成人精品国产免费网站| 国产亚洲欧美色| 国产视频一区二区三区在线播放| 牛牛影视一区二区三区免费看| 久久久久久12| 欧洲av在线播放| 亚洲无人区一区| 亚洲熟女一区二区三区| 欧美一区二区| 成人在线视频网址| 黄色成人在线网| 欧美精品一区二区三区蜜桃视频| 久草免费在线视频观看| 国产成人免费视频网站| 97在线免费视频观看| 久久天堂久久| 国产精品普通话对白| 亚州精品天堂中文字幕| 欧美一级淫片免费视频魅影视频| 一区二区三区蜜桃| 中文字幕99页| 国产欧美精品久久| 欧美日韩综合网| 国产麻豆一区| 久久久av亚洲男天堂| 99国产精品一区二区三区| 亚洲免费观看高清完整版在线观看熊 | 亚洲网站视频福利| 潘金莲一级淫片aaaaaa播放| 中文字幕av不卡| 日本一二三区在线| 伊人精品视频| 热re99久久精品国产99热| 玖玖精品在线| 色av中文字幕一区| 亚洲国产成人精品一区二区三区| 午夜电影久久久| 少妇一级黄色片| 懂色一区二区三区免费观看| 国产91在线免费| 久久激情电影| 国产成人精品一区二区三区福利| 男女羞羞在线观看| 中文字幕不卡av| 午夜精品久久久久久久99老熟妇| 婷婷成人激情在线网| 日本免费www| 国产成人鲁色资源国产91色综| 日韩欧美国产综合在线| 日韩精品永久网址| 国内精品久久国产| 国产成人免费| 97免费在线视频| 男人在线资源站| 日韩av在线免费播放| 亚洲视频一区在线播放| 亚洲成人在线网站| 天堂网av2018| 久久午夜羞羞影院免费观看| 国产精品嫩草影视| 久热国产精品| 男人天堂av片| 亚洲91视频| 欧洲精品久久| 成人偷拍自拍| 成人在线激情视频| 成人国产二区| 韩国久久久久| 国产精品福利小视频| 亚洲wwwww| 这里精品视频免费| 水莓100在线视频| 日韩欧美你懂的| 亚洲视频在线观看一区二区| 精品国产成人av| 欧美 日韩 国产 一区二区三区| 91丨porny丨国产入口| 女教师高潮黄又色视频| 久久视频一区| 黄色一级片国产| 婷婷亚洲最大| 视频一区二区在线观看| 99re91这里只有精品| 成人福利视频在线观看| 欧美free嫩15| 欧美自拍大量在线观看| 国产99re66在线视频| 久久久精品一区二区三区| 黄色av免费在线看| 亚洲第一色中文字幕| 成年丰满熟妇午夜免费视频| 免费毛片在线看片免费丝瓜视频| 色哟哟亚洲精品一区二区| 三级视频网站在线| 亚洲国产精品悠悠久久琪琪| 亚洲AV无码一区二区三区少妇| 5858s免费视频成人| 亚洲一区二区三区高清视频| 欧美体内she精视频| 欧美一区二区三区久久久| 偷窥少妇高潮呻吟av久久免费| 免费在线一级片| 一区二区不卡在线播放| 久久精品一级片| 亚洲一区影音先锋| 国产一级黄色av| 亚洲综合视频在线观看| 欧美日韩精品亚洲精品| 亚洲一区精品在线| 国产网址在线观看| 五月婷婷综合在线| 日韩成人免费在线观看| 欧美日韩免费观看中文| 亚洲欧美一区二区三区在线观看| 色呦呦网站一区| а中文在线天堂| 欧美伦理视频网站| 精品国产九九九| 精品福利在线导航| av女名字大全列表| 亚洲天堂一区二区三区| 91短视频版在线观看www免费| 中文字幕欧美亚洲| gogogogo高清视频在线| 欧美黑人xxxx| 中文在线а√在线8| 国产精品白嫩初高中害羞小美女| 成人做爰视频www| 成人信息集中地欧美| 成人h动漫精品一区二区器材| 精品日本一区二区三区| 国产a久久精品一区二区三区| 色噜噜狠狠一区二区三区| 久久国产小视频| 国产美女主播在线| 香蕉久久国产| 中文字幕成人免费视频| 成人一区二区视频| japanese中文字幕| 亚洲图片你懂的| 国产成人自拍视频在线| 欧美色国产精品| 国产不卡av在线播放| 亚洲精品国产精品国产自| 最新真实国产在线视频| 欧美大片在线免费观看| 九色91porny| 久久国产婷婷国产香蕉| 性猛交╳xxx乱大交| 久久久久久久久久美女| 人妻人人澡人人添人人爽| 欧美日韩人人澡狠狠躁视频| 中文字幕av免费观看| 日韩精品一区二区三区在线播放| 免费一级在线观看| 久久av在线看| 欧美中文字幕精在线不卡| 亚洲综合中文字幕68页| 国产一区二区三区四区| 欧美一二三不卡| 蜜桃视频免费观看一区| 91精品啪在线观看国产| 国产精品传媒入口麻豆| 日本视频网站在线观看| 日韩一级黄色大片| av网页在线| 欧美一区二区影院| 99re8这里有精品热视频8在线| 亚洲人久久久| 新狼窝色av性久久久久久| 欧美日韩一区二区区别是什么 | 成人信息集中地| 精品国产乱码久久久久久婷婷| 国产乱码精品一区二区三区精东| 日韩毛片中文字幕| 99在线视频影院| 999视频在线免费观看| 久久社区一区| 日本熟妇人妻中出| 91女神在线视频| 国产福利久久久| 欧美一级黄色大片| 免费不卡视频| 国产精品一区专区欧美日韩| 啪啪亚洲精品| 一区二区在线观看免费视频播放| 特黄视频免费看| 亚洲国模精品一区| 丁香花在线观看完整版电影| 91久久久久久久久久久久久| 激情婷婷综合| 久久精品99国产| 2021中文字幕一区亚洲| 国产一级做a爱片久久毛片a| 精品动漫一区二区三区在线观看| av色综合久久天堂av色综合在| 国产综合久久久久| 欧美国产一级| 中文字幕在线观看日 | 欧美人妻精品一区二区免费看| 欧美三区免费完整视频在线观看| 激情在线视频| 国产成人欧美在线观看| 国产亚洲一区| 手机看片福利日韩| 国产精品乱码人人做人人爱 | 日韩精品在线免费观看| 日韩伦理精品| 欧美一级日本a级v片| 久久午夜精品一区二区| 国产三级av在线播放| 91国产福利在线| 日本中文字幕电影在线免费观看 | 亚洲精品免费电影| 国产极品久久久| 久久久久久com| 亚洲v天堂v手机在线| 黄色片视频在线播放| 国产欧美日韩不卡| 亚洲视频在线免费播放| 久久伊人免费视频| jazzjazz国产精品久久| 男女超爽视频免费播放| 久久亚洲二区三区| 中文字幕 欧美激情| 26uuu久久综合| 一卡二卡三卡视频| 成人av免费网站| 欧美人一级淫片a免费播放| 日韩中文在线不卡| 欧美成人一级| 免费看一级大黄情大片| 国产欧美一区二区在线观看| 亚洲天堂中文在线| 欧美国产日本在线| 伊人春色精品| 伊人国产精品视频| 亚洲v日本v欧美v久久精品| 久香视频在线观看| 成人日韩在线电影| 日韩一区二区免费看| 亚洲精品91在线| 日韩欧美高清dvd碟片| 韩国美女久久| 国产又粗又爽又黄的视频| 99精品视频一区| 一区二区三区黄| 97精品欧美一区二区三区| 欧美日韩激情| 欧美精品色视频| 色综合久久中文综合久久牛| 日本在线视频网| 久久综合福利| 国产一区二区剧情av在线| 久久久久99精品成人片三人毛片| 久久精品一区中文字幕| 亚洲小说图片视频| 精产国品一二三区| 色94色欧美sute亚洲线路一久 | 亚洲另类春色国产| 国内三级在线观看|