JVM&GC(2)

為什么要學(xué)習(xí)JVM?

jvm和算法是java程序員的內(nèi)功

內(nèi)存與對(duì)象

1.Java內(nèi)存區(qū)域分布于概述

image.png

五個(gè)區(qū)域有各種劃分方式,接下來(lái)我是以生命周期以及線程共享/私有性維度去劃分

與線程生命周期相關(guān)的區(qū)域/線程私有

下面三個(gè)都是隨線程生而生隨線程銷毀而銷毀的哗魂,而且是每個(gè)線程獨(dú)有

  • 程序計(jì)數(shù)器
  • Java虛擬機(jī)棧
  • 本地方法棧

與JVM生命周期相關(guān)的區(qū)域/線程共享

  • 堆:

1.整個(gè)JVM進(jìn)程中只有一個(gè)堆空間怀偷,在JVM啟動(dòng)時(shí)創(chuàng)建抄瓦,JVM關(guān)閉時(shí)銷毀济竹。

  1. 所有線程共享一個(gè)堆空間伟恶,用于存放對(duì)象實(shí)例和數(shù)組惋啃。
  • 方法區(qū):

1.方法區(qū)也與JVM生命周期一致哼鬓,JVM啟動(dòng)時(shí)創(chuàng)建,JVM關(guān)閉時(shí)銷毀边灭。
2.所有線程共享一個(gè)方法區(qū)异希,用于存放類信息、常量绒瘦、靜態(tài)變量等称簿。

程序計(jì)算器

是什么

  • 程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器
  • 線程是一個(gè)獨(dú)立的執(zhí)行單元惰帽,是由CPU控制執(zhí)行的
  • 字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令憨降,分支、循環(huán)该酗、跳轉(zhuǎn)授药、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來(lái)完成
  • 比如有一個(gè)java文件Test.java,程序計(jì)數(shù)器指的不是這個(gè)java文件的某一行代碼,而是這個(gè)java文件編譯后即 Test.class對(duì)應(yīng)的執(zhí)行代碼行數(shù)
    javac Test.java -> Test.class
    為什么

  • 為了線程切換后能恢復(fù)到正確的執(zhí)行位置

  • 每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器呜魄,各條線程之間計(jì)數(shù)器互不影響悔叽,獨(dú)立存儲(chǔ),我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存

特點(diǎn)

  • 內(nèi)存區(qū)域中唯?? 個(gè)沒(méi)有規(guī)定任何 OutOfMemoryError 情況的區(qū)域

JAVA虛擬機(jī)棧

是什么爵嗅?

  • 用于作用于方法執(zhí)行的一塊Java內(nèi)存區(qū)域

為什么娇澎?

  • 每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Framel)
    棧 是一種數(shù)據(jù)結(jié)構(gòu) 先進(jìn)后出
    用于存儲(chǔ)局部變量表、操作數(shù)棧操骡、動(dòng)態(tài)鏈接九火、方法出口等信息。

說(shuō)一下棧幀

  1. 局部變量表(Local Variable Array)
    局部變量表用于存儲(chǔ)方法參數(shù)和方法內(nèi)部定義的局部變量册招。
    它是一個(gè)數(shù)組岔激,索引從0開(kāi)始。對(duì)于實(shí)例方法是掰,索引為0的位置通常存儲(chǔ)this引用虑鼎。

  2. 操作數(shù)棧(Operand Stack)
    操作數(shù)棧用于字節(jié)碼指令的操作數(shù)臨時(shí)存儲(chǔ)。
    在方法執(zhí)行過(guò)程中键痛,字節(jié)碼指令會(huì)將數(shù)據(jù)壓入或彈出操作數(shù)棧炫彩,進(jìn)行計(jì)算或操作。

  3. 動(dòng)態(tài)鏈接(Dynamic Linking)
    動(dòng)態(tài)鏈接用于支持方法調(diào)用過(guò)程中的符號(hào)引用轉(zhuǎn)換為實(shí)際的內(nèi)存地址絮短。
    每個(gè)棧幀包含一個(gè)指向運(yùn)行時(shí)常量池的引用江兢,用于解析方法調(diào)用和字段訪問(wèn)的符號(hào)引用。

  4. 方法出口(Method Return Address)
    方法出口用于保存方法返回地址丁频,當(dāng)一個(gè)方法執(zhí)行完成后需要返回到調(diào)用它的方法時(shí)杉允,程序計(jì)數(shù)器(PC寄存器)需要恢復(fù)到正確的位置繼續(xù)執(zhí)行。

public class JVMStackExample {
    public static void main(String[] args) {
        int result = add(3, 5);
        System.out.println("Result: " + result);
    }

    public static int add(int a, int b) {
        int sum = a + b;
        return sum;
    }
}
//
add方法棧幀
局部變量表:
a:方法參數(shù)(索引0席里,值為3)
b:方法參數(shù)(索引1叔磷,值為5)
sum:局部變量(索引2,值為a + b的結(jié)果8)

操作數(shù)棧:
a和b的值被壓入操作數(shù)棧用于計(jì)算奖磁。
計(jì)算結(jié)果sum被壓入操作數(shù)棧改基,并作為返回值彈出。

動(dòng)態(tài)鏈接:
用于解析加法操作和返回指令咖为。

方法出口:
add方法返回到調(diào)用它的main方法秕狰。
//
main方法棧幀
局部變量表:
args:命令行參數(shù)(索引0)
result:add方法的返回值(索引1)

操作數(shù)棧:
調(diào)用add(3, 5)時(shí),操作數(shù)棧用于傳遞參數(shù)3和5躁染,以及保存返回值封恰。

動(dòng)態(tài)鏈接:
用于解析add方法調(diào)用和System.out.println方法調(diào)用。

方法出口:
main方法返回到JVM引導(dǎo)程序褐啡。

每一個(gè)方法從調(diào)用直至執(zhí)行完成的過(guò)程诺舔,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過(guò)程

特點(diǎn)?

  • 局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean备畦、byte低飒、char、short懂盐、int褥赊、float、long莉恼、double)以及對(duì)象引用(reference 類型)
  • 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度拌喉,將拋出 StackOverflowError 異常
  • b調(diào)用b屬于遞歸調(diào)用會(huì)報(bào)StackOverflowError 棧溢出

本地方法棧

  • 是什么速那?

    • 用于作用域本地方法執(zhí)行的一塊Java內(nèi)存區(qū)域
    • 本地方法棧與Java虛擬機(jī)棧類似,不過(guò)它是為本地方法服務(wù)的尿背。
      一個(gè)本地方法是用非Java語(yǔ)言(通常是C或C++)編寫(xiě)的端仰,并通過(guò)JNI(Java Native Interface)調(diào)用。
  • 為什么田藐?

    • 與Java虛擬機(jī)棧相同荔烧,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Framel)用于存儲(chǔ)局部變量表、操作數(shù)棧汽久、動(dòng)態(tài)鏈接鹤竭、方法出口等信息。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過(guò)程景醇,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過(guò)程
  • 特點(diǎn)臀稚?

    • Hotshot將Java虛擬機(jī)棧和本地方法棧合二為一

  • 是什么?
    • 是Java內(nèi)存區(qū)域中一塊用來(lái)存放對(duì)象實(shí)例的區(qū)域三痰,【幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存】
  • 為什么烁涌?
    • 此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例
    • Java 堆(Java Heap)是 Java 虛擬機(jī)所管理的內(nèi)存中最大的一塊
    • Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域
  • 特點(diǎn)
    • Java 堆是垃圾收集器管理的主要區(qū)域,因此很多時(shí)候也被稱做“GC 堆”
    • -Xmx -Xms 最大和最小堆內(nèi)存
    • Java堆可以分成新生代和老年代 新生代可分為T(mén)o Space酒觅、From Space撮执、Eden

方法區(qū)

  • 是什么?
    • 是各個(gè)線程共享的內(nèi)存區(qū)域舷丹,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息抒钱、常量、靜態(tài)變量颜凯、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
    • 什么是類信息:類版本號(hào)谋币、方法、接口
  • 為什么症概?
    • 內(nèi)存中存放類信息蕾额、靜態(tài)變量等數(shù)據(jù),屬于線程共享的一塊區(qū)域

    • Hotspot使用永久代來(lái)實(shí)現(xiàn)方法區(qū) JRockit彼城、IBM J9VM Java堆一樣管理這部分內(nèi)存

    • 在JDK 8之前诅蝶,方法區(qū)通常被稱為永久代(PermGen),在JDK 8及之后募壕,永久代被移除调炬,改為使用元空間(Metaspace)。元空間不再使用堆內(nèi)存舱馅,而是直接使用本地內(nèi)存缰泡,這大大改善了內(nèi)存管理和性能問(wèn)題。
      方法區(qū)是Java虛擬機(jī)規(guī)范中的一個(gè)邏輯概念
      元空間是HotSpot JVM在JDK 8及以后對(duì)方法區(qū)的一種實(shí)現(xiàn)

    • 為什么移除永久代代嗤?
      永久代存在以下幾個(gè)問(wèn)題:

      • 固定大屑:永久代的大小在啟動(dòng)時(shí)確定缠借,運(yùn)行時(shí)無(wú)法動(dòng)態(tài)調(diào)整,容易導(dǎo)致內(nèi)存溢出宜猜。
      • 垃圾回收復(fù)雜:永久代的垃圾回收與堆的垃圾回收策略不同泼返,導(dǎo)致回收效率低下。
      • 內(nèi)存調(diào)優(yōu)困難:永久代的內(nèi)存調(diào)優(yōu)比較復(fù)雜宝恶,不容易調(diào)整到最優(yōu)狀態(tài)符隙。
  • 元空間解決了這些問(wèn)題:

    • 使用本地內(nèi)存:元空間使用操作系統(tǒng)的本地內(nèi)存趴捅,大小可以動(dòng)態(tài)調(diào)整垫毙。
    • 提高回收效率:元空間的回收與其他區(qū)域(如堆)的回收分開(kāi)進(jìn)行,簡(jiǎn)化了垃圾回收過(guò)程拱绑。
    • 更好的性能和穩(wěn)定性:由于使用本地內(nèi)存综芥,元空間的性能和穩(wěn)定性得到了提升
  • 特點(diǎn)

    • 并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣“永久”存在了。
      這區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載
    • 方法區(qū)也會(huì)拋出OutofMemoryError猎拨,當(dāng)它無(wú)法滿足內(nèi)存分配需求時(shí)
    • 運(yùn)行時(shí)常量池 運(yùn)行時(shí)常量池是方法區(qū)的一部分膀藐,Class文件除了有類的版本、字段红省、方法额各、接口等描述信息外,還有一項(xiàng)信息是常量池吧恃,用于存放編譯器生成的各種字面量和符號(hào)引用虾啦,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。
public class A {

    public static void main(String[] args) {
        String a = "abc";//"abc"存放在方法區(qū)常量池
        String b = "abc";
        System.out.println(a==b);//true
        String c = new String("abc");//存放在堆里
        System.out.println(a==c);//false
        System.out.println(a==c.intern());//true
    }
}

2.對(duì)象是怎么來(lái)的

image.png
  • 1.虛擬機(jī)遇到一條new指令時(shí)痕寓,首先檢查這個(gè)對(duì)應(yīng)的類能否在常量池中定位到一個(gè)類的符號(hào)引用

  • 2.判斷這個(gè)類是否已被加載傲醉、解析和初始化

  • 3.為這個(gè)新生對(duì)象在Java堆中分配內(nèi)存空間,
    其中Java堆分配內(nèi)存空間的方式主要有以下兩種

    • 指針碰撞
      • 分配內(nèi)存空間包括開(kāi)辟一塊內(nèi)存和移動(dòng)指針兩個(gè)步驟
      • 非原子步驟可能出現(xiàn)并發(fā)問(wèn)題呻率,Java虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性(樂(lè)觀鎖原理,下面同理)
    • 空閑列表
      • 分配內(nèi)存空間包括開(kāi)辟一塊內(nèi)存和修改空閑列表兩個(gè)步驟
      • 非原子步驟可能出現(xiàn)并發(fā)問(wèn)題硬毕,Java虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性
  • 4.將分配到的內(nèi)存空間都初始化為零值

  • 設(shè)置對(duì)象頭相關(guān)數(shù)據(jù)

    • GC分代年齡
    • 對(duì)象的哈希碼 hashCode
    • 元數(shù)據(jù)信息
  • 5.執(zhí)行對(duì)象<init>方法

對(duì)象結(jié)構(gòu)

image.png
  • 對(duì)象頭 用于存儲(chǔ)對(duì)象的元數(shù)據(jù)信息

    • Mark Word 部分?jǐn)?shù)據(jù)的長(zhǎng)度在32位和64位虛擬機(jī)(未開(kāi)啟壓縮指針)中分別為32bit和64bit,存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)如哈希值等礼仗。Mark Word一般被設(shè)計(jì)為非固定的數(shù)據(jù)結(jié)構(gòu)吐咳,以便存儲(chǔ)更多的數(shù)據(jù)信息和復(fù)用自己的存儲(chǔ)空間。
    • 類型指針 指向它的類元數(shù)據(jù)的指針元践,用于判斷對(duì)象屬于哪個(gè)類的實(shí)例挪丢。
  • 實(shí)例數(shù)據(jù) 存儲(chǔ)的是真正有效數(shù)據(jù),如各種字段內(nèi)容卢厂,各字段的分配策略為longs/doubles乾蓬、ints、shorts/chars慎恒、bytes/boolean任内、oops(ordinary object pointers)撵渡,相同寬度的字段總是被分配到一起,便于之后取數(shù)據(jù)死嗦。父類定義的變量會(huì)出現(xiàn)在子類定義的變量的前面趋距。

  • 對(duì)齊填充部分 僅僅起到占位符的作用

3.創(chuàng)建了對(duì)象我們是如何訪問(wèn)的

  • 當(dāng)我們?cè)诙焉蟿?chuàng)建一個(gè)對(duì)象實(shí)例后,就要通過(guò)虛擬機(jī)棧中的reference類型數(shù)據(jù)來(lái)操作堆上的對(duì)象越除。現(xiàn)在主流的訪問(wèn)方式有兩種(HotSpot虛擬機(jī)采用的是第二種)
    1. 使用句柄訪問(wèn)對(duì)象节腐。即reference中存儲(chǔ)的是對(duì)象句柄的地址,而句柄中包含了對(duì)象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)的具體地址信息摘盆,相當(dāng)于二級(jí)指針翼雀。
    2. 直接指針訪問(wèn)對(duì)象。即reference中存儲(chǔ)的就是對(duì)象地址孩擂,相當(dāng)于一級(jí)指針狼渊。
  • 對(duì)比
    從垃圾回收的角度句柄訪問(wèn)更優(yōu)
    從訪問(wèn)效率的角度直接指針訪問(wèn)更優(yōu)
    • 垃圾回收分析:方式1??當(dāng)垃圾回收移動(dòng)對(duì)象時(shí),reference中存儲(chǔ)的地址是穩(wěn)定的地址类垦,不需要修改狈邑,僅需要修改對(duì)象句柄的地址;方式2??垃圾回收時(shí)需要修改reference中存儲(chǔ)的地址蚤认。

    • 訪問(wèn)效率分析米苹,方式二優(yōu)于方式一,因?yàn)榉绞蕉贿M(jìn)行了一次指針定位砰琢,節(jié)省了時(shí)間開(kāi)銷蘸嘶,而這也是HotSpot采用的實(shí)現(xiàn)方式。

句柄訪問(wèn)對(duì)象


image.png

直接指針訪問(wèn)對(duì)象


image.png

垃圾回收

1.什么是垃圾回收

  • 戰(zhàn)略意義 能做出一個(gè)需求的同時(shí)也要懂得其對(duì)應(yīng)的戰(zhàn)略意義

  • 為什么要垃圾回收氯析?

    • Java語(yǔ)言中一個(gè)顯著的特點(diǎn)就是引入了垃圾回收機(jī)制亏较,使c++程序員最頭疼的內(nèi)存管理的問(wèn)題迎刃而解。由于有個(gè)垃圾回收機(jī)制掩缓,Java中的對(duì)象不再有“作用域”的概念雪情,只有對(duì)象的引用才有“作用域”。垃圾回收可以有效的防止內(nèi)存泄露你辣,有效的使用空閑的內(nèi)存
  • 垃圾回收的過(guò)程是怎樣的巡通?

      1. 對(duì)象都是放在堆里面,所以絕大部分(幾乎所有)的垃圾回收進(jìn)行在堆
    
      2.怎么觸發(fā)?觸發(fā)機(jī)制
    
              2.1 想要放一個(gè)內(nèi)存但是放不進(jìn)去
    
              2.2 堆空間達(dá)到一個(gè)比例的時(shí)候觸發(fā)
    
  • 如果讓你考慮垃圾回收算法你會(huì)怎么設(shè)計(jì)

    • 完成哪些對(duì)象回收哪些對(duì)象不回收的功能需求

      通過(guò)算法 例如通過(guò)引用計(jì)數(shù)法 簡(jiǎn)單來(lái)說(shuō)調(diào)用的時(shí)候+1舍哄,斷開(kāi)調(diào)用-1

  • 對(duì)象是否存活判斷

    • 堆中每個(gè)對(duì)象實(shí)例都有一個(gè)引用計(jì)數(shù)宴凉。當(dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),且將該對(duì)象實(shí)例分配給一個(gè)變量表悬,該變量計(jì)數(shù)設(shè)置為1弥锄。
    • 當(dāng)任何其它變量被賦值為這個(gè)對(duì)象的引用時(shí),計(jì)數(shù)加1(a = b,則b引用的對(duì)象實(shí)例的計(jì)數(shù)器+1),
    • 但當(dāng)一個(gè)對(duì)象實(shí)例的某個(gè)引用超過(guò)了生命周期或者被設(shè)置為一個(gè)新值時(shí)籽暇,對(duì)象實(shí)例的引用計(jì)數(shù)器減1温治。
    • 任何引用計(jì)數(shù)器為0的對(duì)象實(shí)例可以被當(dāng)作垃圾收集。當(dāng)一個(gè)對(duì)象實(shí)例被垃圾收集時(shí)戒悠,它引用的任何對(duì)象實(shí)例的引用計(jì)數(shù)器減1

2.什么時(shí)候被回收

對(duì)象存活算法之引用計(jì)數(shù)法

  • 引用計(jì)數(shù)法存在的特點(diǎn)分析

  • 優(yōu)缺點(diǎn)

    • 優(yōu)點(diǎn):引用計(jì)數(shù)收集器可以很快的執(zhí)行熬荆,交織在程序運(yùn)行中。對(duì)程序需要不被長(zhǎng)時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利绸狐。
    • 缺點(diǎn):無(wú)法檢測(cè)出循環(huán)引用卤恳。如父對(duì)象有一個(gè)對(duì)子對(duì)象的引用,子對(duì)象反過(guò)來(lái)引用父對(duì)象寒矿。這樣突琳,他們的引用計(jì)數(shù)永遠(yuǎn)不可能為0.


      image.png

對(duì)象存活之可達(dá)性分析

可達(dá)性算法解決了上面引用計(jì)數(shù)的缺點(diǎn)問(wèn)題

  • 可達(dá)性分析算法的概念(又叫跟搜索法)

    • 根搜索算法是從離散數(shù)學(xué)中的圖論引入的,程序把所有的引用關(guān)系看作一張圖劫窒,從一個(gè)節(jié)點(diǎn)GC ROOT開(kāi)始本今,尋找對(duì)應(yīng)的引用節(jié)點(diǎn)拆座,找到這個(gè)節(jié)點(diǎn)以后主巍,繼續(xù)尋找這個(gè)節(jié)點(diǎn)的引用節(jié)點(diǎn),當(dāng)所有的引用節(jié)點(diǎn)尋找完畢之后挪凑,剩余的節(jié)點(diǎn)則被認(rèn)為是沒(méi)有被引用到的節(jié)點(diǎn)孕索,即無(wú)用的節(jié)點(diǎn)
      一個(gè)對(duì)象從某個(gè)根節(jié)點(diǎn)開(kāi)始,任何一條引用鏈能引用到的
  • java中可作為GC Root的對(duì)象有

    • 虛擬機(jī)棧中引用的對(duì)象(本地變量表)

    • 本地方法棧中引用的對(duì)象

    • 方法區(qū)中靜態(tài)屬性引用的對(duì)象

    • 方法區(qū)中常量引用的對(duì)象

3.回收算法

3.1標(biāo)記清除

標(biāo)記-清除算法是垃圾回收機(jī)制中的一種基礎(chǔ)算法。它包括兩個(gè)主要階段:標(biāo)記(Mark)和清除(Sweep)躏碳。

    1. 標(biāo)記階段
      在標(biāo)記階段搞旭,垃圾回收器會(huì)遍歷所有可達(dá)對(duì)象,并將其標(biāo)記為“存活”菇绵。遍歷從根集合(Root Set)開(kāi)始肄渗,根集合通常包括棧中的引用、全局靜態(tài)變量和寄存器中的引用咬最。所有從根集合可以訪問(wèn)到的對(duì)象都被認(rèn)為是存活的翎嫡。
    1. 清除階段
      在清除階段,垃圾回收器會(huì)遍歷堆中的所有對(duì)象永乌,清除未被標(biāo)記的對(duì)象惑申,即那些不可達(dá)的對(duì)象。被清除的對(duì)象的內(nèi)存會(huì)被釋放翅雏,以供新對(duì)象分配使用圈驼。

標(biāo)記-清除算法的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn)
    簡(jiǎn)單:算法邏輯簡(jiǎn)單,容易實(shí)現(xiàn)望几。
    無(wú)需移動(dòng)對(duì)象:清除階段只是釋放未標(biāo)記對(duì)象的內(nèi)存绩脆,而不需要移動(dòng)對(duì)象,因此減少了內(nèi)存復(fù)制的開(kāi)銷。
  • 缺點(diǎn)
    內(nèi)存碎片:清除階段后靴迫,釋放的內(nèi)存塊可能是不連續(xù)的祈坠,導(dǎo)致內(nèi)存碎片問(wèn)題。這會(huì)使得大對(duì)象的分配變得困難矢劲。
    停頓時(shí)間長(zhǎng):標(biāo)記和清除階段需要暫停應(yīng)用程序(即STW赦拘,Stop-The-World),對(duì)實(shí)時(shí)性要求高的應(yīng)用影響較大芬沉。

3.2復(fù)制算法

  • 為甚么出現(xiàn)復(fù)制算法躺同?
    • 為了解決效率問(wèn)題,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了丸逸,它將可用內(nèi)存按量劃分為大小相等的兩塊蹋艺,每次只使用其中的一塊
    • 當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面黄刚,然后再把已使用過(guò)的內(nèi)存空間一次清理掉捎谨。這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況憔维,只要移動(dòng)堆頂指針涛救,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單业扒,運(yùn)行高效
  • 現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來(lái)回收新生代检吆,研究表明,新生代中的對(duì)象 98%是“朝生夕死”的程储,所以并不需要按照 1:1 的比例來(lái)劃分內(nèi)存空間蹭沛,而是將內(nèi)存分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor章鲤。 Survivor from 和Survivor to 摊灭,內(nèi)存比例 8:1:1
  • 當(dāng)回收時(shí),將 Eden 和 Survivor 中還存活著的對(duì)象一次性地復(fù)制到另外一塊 Survivor 空間上败徊,最后清理掉 Eden 和剛才用過(guò)的 Survivor 空間帚呼。HotSpot 虛擬機(jī)默認(rèn) Eden 和 Survivor 的大小比例是 8:1, 也就是每次新生代中可用內(nèi)存空間為整個(gè)新生代容量的 90% (80%+10%),只有 10% 的內(nèi)存會(huì)被“浪費(fèi)”集嵌。當(dāng)然萝挤,98%的對(duì)象可回收只是一般場(chǎng)景下的數(shù)據(jù),我們沒(méi)有辦法保證每次回收都只有不多于 10%的對(duì)象存活根欧,當(dāng) Survivor 空間不夠用時(shí)怜珍,需要依賴其他內(nèi)存(這里指老年代)進(jìn)行分配擔(dān)保(Handle Promotion)。
    上面和下面各自為S1和S2


    image.png

復(fù)制算法(Copying Algorithm)
復(fù)制算法(Copying Algorithm)是一種垃圾回收算法凤粗,通過(guò)將存活對(duì)象從一個(gè)內(nèi)存區(qū)域復(fù)制到另一個(gè)內(nèi)存區(qū)域酥泛,從而有效地管理內(nèi)存并避免內(nèi)存碎片問(wèn)題今豆。該算法通常用于新生代(Young Generation)的垃圾回收。

基本原理
復(fù)制算法將堆內(nèi)存分為兩個(gè)等大的空間:from-space和to-space柔袁。在任何時(shí)間點(diǎn)呆躲,一個(gè)空間是活動(dòng)的,另一個(gè)空間是空閑的捶索。當(dāng)垃圾回收發(fā)生時(shí)插掂,所有存活對(duì)象從活動(dòng)空間復(fù)制到空閑空間,并釋放整個(gè)活動(dòng)空間腥例。

  • 步驟
    1.初始狀態(tài):對(duì)象分配在活動(dòng)空間(from-space)辅甥。
    2.標(biāo)記存活對(duì)象:從根集合開(kāi)始,標(biāo)記所有存活對(duì)象燎竖。
    3.復(fù)制存活對(duì)象:將存活對(duì)象從活動(dòng)空間復(fù)制到空閑空間(to-space)璃弄,并更新相應(yīng)的引用。
    4.交換空間:空閑空間變?yōu)樾碌幕顒?dòng)空間构回,舊的活動(dòng)空間被清空夏块,成為新的空閑空間。
  • 優(yōu)點(diǎn)
    消除內(nèi)存碎片:通過(guò)復(fù)制存活對(duì)象到連續(xù)的內(nèi)存區(qū)域纤掸,有效地消除內(nèi)存碎片脐供。
    分配速度快:由于總是從連續(xù)的空閑區(qū)域分配內(nèi)存,內(nèi)存分配速度非匙鲁Γ快患民。
  • 缺點(diǎn)
    內(nèi)存開(kāi)銷大:需要雙倍的內(nèi)存空間(from-space和to-space)缩举,內(nèi)存利用率較低垦梆。
    復(fù)制開(kāi)銷:存活對(duì)象的復(fù)制會(huì)增加一定的開(kāi)銷,尤其是在存活對(duì)象較多時(shí)仅孩。

本質(zhì)就是 空間換時(shí)間

3.3標(biāo)記整理算法與分代收集算法

  • 標(biāo)記整理算法解決了什么問(wèn)題

    • 復(fù)制收集算法在對(duì)象存活率較高時(shí)就要進(jìn)行較多的復(fù)制操作托猩,效率將會(huì)變低。更關(guān)鍵的是辽慕,如果不想浪費(fèi) 50%的空間京腥,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都 100%存活的極端情況溅蛉,所以在老年代一般不能直接選用這種算法
  • 標(biāo)記-整理

    • 根據(jù)老年代的特點(diǎn)公浪,有人提出了另外一種“標(biāo)記-整理(Mark- Compact)算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣船侧,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理欠气,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存
  • 分代收集
    • 一般把 Java 堆分為新生代和老年代镜撩,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴?/li>
    • 在新生代中预柒,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,那就選用復(fù)制算法宜鸯,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集憔古。
      而老年代中因?yàn)閷?duì)象存活率高、沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保淋袖,就必須使用“標(biāo)記-清理”或者“標(biāo)記一整理”算法來(lái)進(jìn)行回收

4.垃圾回收器有哪些

  • JDK 8
    默認(rèn)垃圾回收器:Parallel GC
    可選垃圾回收器:
    Serial GC
    Parallel GC
    CMS(Concurrent Mark-Sweep)GC
    G1(Garbage First)GC(可以選擇使用鸿市,但不是默認(rèn))
  • JDK 9
    默認(rèn)垃圾回收器:G1 GC
    可選垃圾回收器:
    Serial GC
    Parallel GC
    CMS GC
    G1 GC
  • JDK 11
    默認(rèn)垃圾回收器:G1 GC
    可選垃圾回收器:
    Serial GC
    Parallel GC
    CMS GC
    G1 GC
    ZGC(Z Garbage Collector)
  • JDK 12
    默認(rèn)垃圾回收器:G1 GC
    可選垃圾回收器:
    Serial GC
    Parallel GC
    CMS GC(JDK 14開(kāi)始廢棄)
棄用原因:
隨著 JDK 版本的迭代,CMS 收集器的停頓時(shí)間較長(zhǎng)即碗,在大堆內(nèi)存應(yīng)用中性能表現(xiàn)不佳灸芳,逐漸被 G1 收集器替代。

G1 GC
ZGC
Shenandoah GC

  • 詳細(xì)說(shuō)明

JDK 8:默認(rèn)使用Parallel GC拜姿,用戶可以選擇Serial GC烙样、CMS GC或G1 GC。

JDK 9:G1 GC成為默認(rèn)垃圾回收器蕊肥,因?yàn)樗峁┝烁玫男阅芎洼^短的停頓時(shí)間谒获,尤其適合大堆內(nèi)存的應(yīng)用程序。但其他垃圾回收器仍然可用壁却。

JDK 11:繼續(xù)以G1 GC作為默認(rèn)垃圾回收器批狱,并引入了ZGC。ZGC設(shè)計(jì)用于超大堆內(nèi)存展东,目標(biāo)是提供低于10ms的停頓時(shí)間赔硫。其他垃圾回收器仍然可用。

JDK 12及之后:默認(rèn)仍為G1 GC盐肃,但增加了Shenandoah GC爪膊,Shenandoah GC的目標(biāo)是實(shí)現(xiàn)更低的停頓時(shí)間,適用于大堆內(nèi)存應(yīng)用砸王。CMS GC在JDK 14中被廢棄推盛,但在之前版本中仍可用。

每種垃圾回收器有哪些特色

Serial 收集器(Serial GC):

它是一種單線程的垃圾回收器谦铃,使用標(biāo)記-復(fù)制算法進(jìn)行新生代的垃圾回收耘成,使用標(biāo)記-清除算法進(jìn)行老年代的垃圾回收。適用于小型或中等規(guī)模的應(yīng)用驹闰。

優(yōu)點(diǎn):簡(jiǎn)單高效瘪菌,適用于單核或小型應(yīng)用。
缺點(diǎn):?jiǎn)尉€程處理嘹朗,無(wú)法充分利用多核處理器师妙。

serial垃圾收集器的特點(diǎn)

  • “Stop The World”,它進(jìn)行垃圾收集時(shí)骡显,必須暫停其他所有的工作線程疆栏,直到它收集結(jié)束曾掂。在用戶不可見(jiàn)的情況下把用戶正常工作的線程全部停掉
  • 使用場(chǎng)景:多用于桌面應(yīng)用,Client端的垃圾回收器
  • 桌面應(yīng)用內(nèi)存小壁顶,進(jìn)行垃圾回收的時(shí)間比較短珠洗,只要不頻繁發(fā)生停頓就可以接受
Parallel 收集器(Parallel GC):

ParNew 收集器其實(shí)就是 Serial 收集器的多線程版本
ParNew 收集器除了多線程收集之外,其他與 Serial 收集器相比并沒(méi)有太多創(chuàng)新之處若专,但它卻是許多運(yùn)行在 Server 模式下的虛擬機(jī)中首選的新生代收集器许蓖,其中有一個(gè)與性能無(wú)關(guān)但很重要的原因是,除了 Serial 收集器外调衰,目前只有它能與 CMS 收集器配合工作膊爪。

  • 使用-XX: ParallelGCThreads 參數(shù)來(lái)限制垃圾收集的線程數(shù)
  • 多線程操作存在上下文切換的問(wèn)題,所以建議將-XX: ParallelGCThreads設(shè)置成和CPU核數(shù)相同嚎莉,如果設(shè)置太多的話就會(huì)產(chǎn)生上下文切換消耗
    它是一種多線程的垃圾回收器米酬,也稱為吞吐量?jī)?yōu)先的垃圾回收器。在新生代使用標(biāo)記-復(fù)制算法趋箩,在老年代使用標(biāo)記-清除算法赃额。適用于多核服務(wù)器應(yīng)用,可提高吞吐量叫确。

優(yōu)點(diǎn):多線程處理跳芳,適用于多核處理器,提高吞吐量竹勉。
缺點(diǎn):全停頓飞盆,可能造成較長(zhǎng)的停頓時(shí)間。

CMS 收集器(Concurrent Mark-Sweep GC):

它是一種并發(fā)垃圾回收器次乓,使用標(biāo)記-清除算法吓歇。在并發(fā)標(biāo)記和并發(fā)清除階段盡量減少應(yīng)用程序停頓時(shí)間。適用于需要較短停頓時(shí)間的應(yīng)用檬输。

優(yōu)點(diǎn):并發(fā)標(biāo)記和清除照瘾,減少停頓時(shí)間。
缺點(diǎn):產(chǎn)生碎片丧慈,可能導(dǎo)致 Full GC 頻繁執(zhí)行,吞吐量不如 Parallel 收集器高主卫。

  • 對(duì)CPU資源非常敏感
  • 無(wú)法處理浮動(dòng)垃圾逃默,程序在進(jìn)行并發(fā)清除階段用戶線程所產(chǎn)生的新垃圾
  • 標(biāo)記-清除暫時(shí)空間碎片
G1 收集器(Garbage-First GC):

它是一種面向整個(gè)堆的并發(fā)垃圾回收器,適用于大堆內(nèi)存應(yīng)用簇搅。使用分代收集的思想完域,將堆內(nèi)存分為若干個(gè)大小相等或相近的區(qū)域,實(shí)現(xiàn)了更靈活和高效的垃圾回收瘩将。在垃圾回收過(guò)程中吟税,G1優(yōu)先選擇包含垃圾最多的區(qū)域凹耙,并將其中的存活對(duì)象復(fù)制到空閑區(qū)域。

優(yōu)點(diǎn):面向大堆內(nèi)存肠仪,可預(yù)測(cè)的停頓時(shí)間肖抱。

  • 空間整合:基于“標(biāo)記一整理”算法實(shí)現(xiàn)為主和Region之間采用復(fù)制算法實(shí)現(xiàn)的垃圾收集
  • 可預(yù)測(cè)的停頓:這是 G1 相對(duì)于 CMS 的另一大優(yōu)勢(shì),降低停頓時(shí)間是 G1 和 CMS 共同的關(guān)注點(diǎn)异旧,但 G1 除了追求低停頓外意述,還能建立可預(yù)測(cè)的停頓時(shí)間模型
  • 在 G1 之前的其他收集器進(jìn)行收集的范圍都是整個(gè)新生代或者老年代,而 G1 不再是這樣吮蛹。使用 G1 收集器時(shí)荤崇,Java 堆的內(nèi)存布局就與其他收集器有很大差別,它將整個(gè) Java 雄劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)潮针,雖然還保留有新生代和老年代的概念术荤,但新生代和老年代不再是物理隔髙的了,它們都是一部分 Region(不需要連續(xù))的集合每篷。
  • G1 收集器之所以能建立可預(yù)測(cè)的停頓時(shí)間模型喜每,是因?yàn)樗梢杂杏?jì)劃地避免在整個(gè) Java 堆中進(jìn)行全區(qū)域的垃圾收集。G1 跟蹤各個(gè) Regions 里面的垃圾堆積的價(jià)值大婿ㄈ痢(回收所獲得的空間大小以及回收所需時(shí)間的經(jīng)驗(yàn)值)带兜,在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間吨灭,優(yōu)先回收價(jià)值最大的 Region(這也就是 Garbage- Firsti 名稱的來(lái)由)刚照。這種使用 Region 劃分內(nèi)存空間以及有優(yōu)先級(jí)的區(qū)域回收方式,保證了 G1 收集器在有限的時(shí)間內(nèi)可以獲取盡可能高
    缺點(diǎn):初始標(biāo)記和最終標(biāo)記暫停時(shí)間較長(zhǎng)喧兄,吞吐量相對(duì)較低无畔。
ZGC(Z Garbage Collector):

ZGC是一種低延遲的、可伸縮的垃圾回收器吠冤,旨在減少GC停頓時(shí)間并提供可預(yù)測(cè)的性能浑彰。它是一種并發(fā)垃圾回收器,能夠在幾毫秒甚至數(shù)毫秒的時(shí)間內(nèi)處理幾十GB甚至數(shù)百GB的堆內(nèi)存拯辙。ZGC適用于需要低延遲和大內(nèi)存堆的應(yīng)用場(chǎng)景郭变,比如金融、游戲和電商等領(lǐng)域涯保。

Shenandoah GC:

Shenandoah GC是一種低停頓時(shí)間的垃圾回收器诉濒,旨在減少GC停頓時(shí)間,并提供可預(yù)測(cè)的性能夕春。它是一種并發(fā)垃圾回收器未荒,能夠在幾毫秒的時(shí)間內(nèi)處理幾十GB甚至數(shù)百GB的堆內(nèi)存。Shenandoah GC適用于需要低停頓時(shí)間和大內(nèi)存堆的應(yīng)用場(chǎng)景及志,比如云計(jì)算和大數(shù)據(jù)等領(lǐng)域片排。

Epsilon GC:

Epsilon GC是一種實(shí)驗(yàn)性的垃圾回收器寨腔,它完全不進(jìn)行內(nèi)存回收。Epsilon GC適用于特定的測(cè)試場(chǎng)景和性能調(diào)優(yōu)場(chǎng)景率寡,可以用來(lái)驗(yàn)證GC對(duì)應(yīng)用程序性能的影響迫卢,或者用作一種特殊的性能優(yōu)化工具。

堆內(nèi)存到底是怎么分配

image.png

Java堆內(nèi)存是運(yùn)行時(shí)數(shù)據(jù)區(qū)域勇劣,所有類的實(shí)例和數(shù)組在堆中分配內(nèi)存靖避。堆內(nèi)存根據(jù)分代垃圾回收的理念分為不同的區(qū)域,以優(yōu)化垃圾回收過(guò)程比默。這些區(qū)域主要包括新生代(Young Generation)幻捏、老年代(Old Generation),以及在JDK 8之前的永久代(Permanent Generation)或在JDK 8之后的元空間(Metaspace)命咐。以下是對(duì)這些區(qū)域的詳細(xì)解釋及其內(nèi)存分配方式:

新生代(Young Generation)

新生代主要存放短生命周期的對(duì)象篡九。它進(jìn)一步細(xì)分為以下三個(gè)區(qū)域:

  1. Eden區(qū):所有的新對(duì)象首先在Eden區(qū)分配內(nèi)存。
  2. Survivor區(qū):新生代包含兩個(gè)Survivor區(qū)醋奠,分別是Survivor 0(S0)和Survivor 1(S1)榛臼。在進(jìn)行垃圾回收時(shí),Eden區(qū)和一個(gè)Survivor區(qū)中的存活對(duì)象將被復(fù)制到另一個(gè)Survivor區(qū)窜司。

新生代垃圾回收(Minor GC)

  • 當(dāng)Eden區(qū)填滿時(shí)沛善,會(huì)觸發(fā)一次Minor GC。在這次回收過(guò)程中塞祈,存活的對(duì)象會(huì)被復(fù)制到空閑的Survivor區(qū)(S0或S1)金刁,Eden區(qū)和被使用的Survivor區(qū)將被清空。
  • 經(jīng)歷過(guò)多次Minor GC且仍然存活的對(duì)象將被晉升到老年代议薪。

老年代(Old Generation)

老年代用于存放生命周期較長(zhǎng)的對(duì)象尤蛮。新生代中的對(duì)象經(jīng)過(guò)多次Minor GC仍然存活后溪北,會(huì)被移到老年代潘靖。老年代的空間相對(duì)較大,但垃圾回收的頻率較低贼穆,采用的垃圾回收算法通常是標(biāo)記-清除或標(biāo)記-整理哼御。

老年代垃圾回收(Major GC / Full GC)

  • 老年代的垃圾回收頻率較低坯临,但回收過(guò)程會(huì)引起較長(zhǎng)時(shí)間的應(yīng)用停頓。
  • 老年代的垃圾回收通常是標(biāo)記-清除(Mark-Sweep)或標(biāo)記-整理(Mark-Compact)算法艇搀。

永久代(Permanent Generation)

在JDK 8之前尿扯,永久代用于存放類的元數(shù)據(jù),如類的結(jié)構(gòu)信息焰雕、方法信息和常量池。永久代的大小是固定的芳杏,由-XX:PermSize-XX:MaxPermSize參數(shù)設(shè)置矩屁。

JDK 8之后:元空間(Metaspace)

  • 從JDK 8開(kāi)始辟宗,永久代被移除,改為元空間(Metaspace)吝秕。元空間不在堆內(nèi)存中泊脐,而是在本地內(nèi)存中分配。
  • 元空間的大小可以通過(guò)-XX:MetaspaceSize-XX:MaxMetaspaceSize參數(shù)進(jìn)行配置烁峭。
  • 元空間的動(dòng)態(tài)擴(kuò)展解決了永久代可能出現(xiàn)的OutOfMemoryError問(wèn)題容客。

內(nèi)存分配示例

以下是一個(gè)示例代碼和堆內(nèi)存分配的解釋:

public class HeapMemoryAllocation {
    public static void main(String[] args) {
        // 對(duì)象分配在Eden區(qū)
        byte[] array1 = new byte[1024 * 1024]; // 1 MB
        byte[] array2 = new byte[1024 * 1024]; // 1 MB
        
        // 觸發(fā)Minor GC
        byte[] array3 = new byte[1024 * 1024]; // 1 MB
        // 在Minor GC后,存活對(duì)象被復(fù)制到Survivor區(qū)

        // 對(duì)象晉升到老年代
        for (int i = 0; i < 10; i++) {
            byte[] array4 = new byte[1024 * 1024]; // 1 MB
        }
        // 在多次Minor GC后约郁,存活對(duì)象晉升到老年代
    }
}

在這個(gè)示例中:

  • array1array2對(duì)象首先分配在Eden區(qū)缩挑。
  • 創(chuàng)建array3對(duì)象后,Eden區(qū)可能會(huì)滿鬓梅,觸發(fā)一次Minor GC供置,將存活的array1array2復(fù)制到Survivor區(qū)。
  • 在循環(huán)中創(chuàng)建array4對(duì)象绽快,可能會(huì)觸發(fā)多次Minor GC芥丧,最終將一些存活對(duì)象晉升到老年代。

總結(jié)

  • 新生代:包括Eden區(qū)和兩個(gè)Survivor區(qū)坊罢,用于存放短生命周期對(duì)象续担,采用標(biāo)記-復(fù)制算法。
  • 老年代:存放生命周期較長(zhǎng)的對(duì)象活孩,采用標(biāo)記-清除或標(biāo)記-整理算法物遇。
  • 永久代/元空間:存放類元數(shù)據(jù),從JDK 8開(kāi)始由永久代改為元空間诱鞠,使用本地內(nèi)存進(jìn)行分配挎挖。

內(nèi)存分配和垃圾回收策略旨在優(yōu)化應(yīng)用程序性能,減少停頓時(shí)間航夺,提高內(nèi)存使用效率蕉朵。不同的垃圾回收器可能有不同的實(shí)現(xiàn)細(xì)節(jié),但基本思想是類似的阳掐。

大對(duì)象的分配

對(duì)象大小超過(guò)了堆的10%始衅,即一個(gè)S1的空間,會(huì)直接分配到老年代

FullGC與MinorGC

  • Minor GC觸發(fā)條件:當(dāng)Eden區(qū)滿時(shí)缭保,觸發(fā)Minor GC

  • FullGC觸發(fā)條件

    • 調(diào)用 System.gc()
      此方法的調(diào)用是建議 JVM 進(jìn)行 Full GC汛闸,雖然只是建議而非一定,但很多情況下它會(huì)觸發(fā) Full GC艺骂。因此強(qiáng)烈建議能不使用此方法就不要使用诸老,讓虛擬機(jī)自己去管理它的內(nèi)存∏。可通過(guò) -XX:+ DisableExplicitGC 來(lái)禁止 RMI 調(diào)用 System.gc()

    • 老年代空間不足
      老年代空間不足的常見(jiàn)場(chǎng)景為前文所講的大對(duì)象直接進(jìn)入老年代别伏、長(zhǎng)期存活的對(duì)象進(jìn)入老年代等蹄衷,當(dāng)執(zhí)行 Full GC 后空間仍然不足,則拋出 Java.lang.OutOfMemoryError厘肮。為避免以上原因引起的 Full GC愧口,調(diào)優(yōu)時(shí)應(yīng)盡量做到讓對(duì)象在 Minor GC 階段被回收、讓對(duì)象在新生代多存活一段時(shí)間以及不要?jiǎng)?chuàng)建過(guò)大的對(duì)象及數(shù)組

    • 空間分配擔(dān)保失敗
      使用復(fù)制算法的 Minor GC 需要老年代的內(nèi)存空間作擔(dān)保类茂,如果出現(xiàn)了 HandlePromotionFailure 擔(dān)保失敗耍属,則會(huì)觸發(fā) Full GC

  • 此項(xiàng)目中出現(xiàn)頻繁FullGC,也就是系統(tǒng)空間分配不足導(dǎo)致的系統(tǒng)堆內(nèi)存強(qiáng)制回收

  • 問(wèn)題解決方法分析

    • 由于本機(jī)單服務(wù)內(nèi)存過(guò)大導(dǎo)致巩检,此場(chǎng)景下Full GC厚骗,而且需要回收的內(nèi)存很大,持續(xù)時(shí)間過(guò)長(zhǎng)
    • 解決停頓時(shí)間過(guò)長(zhǎng)問(wèn)題碴巾,縮短GC時(shí)間
      即調(diào)優(yōu)目的為減少Full GC次數(shù)以及 一次Full GC時(shí)間

解決Full GC 方案一
假設(shè)是單機(jī)32G內(nèi)存 -那么默認(rèn)最大堆內(nèi)存為30G 即Xmx30G

  • 32G內(nèi)存-xmx30G溯捆,系統(tǒng)每次進(jìn)行FullGC時(shí)長(zhǎng)太長(zhǎng)
  • 可以減少-xmx大小成4G,從而縮短Full GC
  • 最終解決方案:集群部署厦瓢,第一個(gè)節(jié)點(diǎn)4G 第二個(gè)節(jié)點(diǎn)4G 第三個(gè)節(jié)點(diǎn)4G 用nginx配置轉(zhuǎn)發(fā) upstream

互聯(lián)網(wǎng)項(xiàng)目常見(jiàn)JVM問(wèn)題講述

  • 為什么訪問(wèn)量大就越容易出問(wèn)題
    • 判斷一個(gè)用戶是否在白名單 List.contain(用戶) true提揍,==》Set.contain(用戶) 通過(guò)hash比較==》布隆過(guò)濾器
    • 結(jié)論==》用戶量大和用戶量小的項(xiàng)目遇到的問(wèn)題和解決方案也就不一樣
  • 案例1,關(guān)于死鎖問(wèn)題
    • 解決方案==》jstack -m命令查看幫我們檢測(cè)是否有死鎖煮仇,或者jconsole劳跃、jvisualVM也能檢查
    • new Thread的時(shí)候最好帶上名稱
  • 案例2,堆內(nèi)存泄漏問(wèn)題
    • 現(xiàn)象:出現(xiàn)OOM或者Full GC浙垫,heap使用率明顯上升刨仑,經(jīng)常達(dá)到Xmx
    • Full GC出現(xiàn)的正常頻率是大概一天一到兩次
    • 解決方案==》jmap dump下內(nèi)存/heap dump on OOM/heap dump on FullGC+jhat本地分析,
      或者jconsole夹姥、jvisualVM也能檢查
  • 案例3杉武,堆外內(nèi)存泄漏

    • 現(xiàn)象:heap使用率很低,但是出現(xiàn)了OOM或者Full GC

    • 解決方案==》可以用btrace跟蹤DirectByteBuffer的構(gòu)造函數(shù)來(lái)定位

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辙售,一起剝皮案震驚了整個(gè)濱河市轻抱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旦部,老刑警劉巖祈搜,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異士八,居然都是意外死亡容燕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)婚度,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蘸秘,“玉大人,你說(shuō)我怎么就攤上這事∶匮” “怎么了味抖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵评甜,是天一觀的道長(zhǎng)灰粮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)忍坷,這世上最難降的妖魔是什么粘舟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮佩研,結(jié)果婚禮上柑肴,老公的妹妹穿的比我還像新娘。我一直安慰自己旬薯,他們只是感情好晰骑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著绊序,像睡著了一般硕舆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骤公,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天抚官,我揣著相機(jī)與錄音,去河邊找鬼阶捆。 笑死凌节,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洒试。 我是一名探鬼主播倍奢,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼垒棋!你這毒婦竟也來(lái)了卒煞?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捕犬,失蹤者是張志新(化名)和其女友劉穎跷坝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體碉碉,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柴钻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了垢粮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贴届。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毫蚓,到底是詐尸還是另有隱情占键,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布元潘,位于F島的核電站畔乙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏翩概。R本人自食惡果不足惜牲距,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钥庇。 院中可真熱鬧牍鞠,春花似錦、人聲如沸评姨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吐句。三九已至胁后,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕴侧,已是汗流浹背择同。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留净宵,地道東北人敲才。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像择葡,于是被迫代替她去往敵國(guó)和親紧武。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1敏储、對(duì)象存活 內(nèi)存回收與分配重點(diǎn)關(guān)注的是堆內(nèi)存和方法區(qū)內(nèi)存(程序計(jì)數(shù)器占用小阻星,虛擬機(jī)棧和本地方法棧隨線程有相同的生...
    橋頭放牛娃閱讀 431評(píng)論 0 5
  • 分區(qū)(Heap Region,HR) 分區(qū)類型 自由分區(qū)(Free Heap Region已添,F(xiàn)HR) 新生代分區(qū)(...
    偉大的卷發(fā)閱讀 494評(píng)論 0 0
  • 內(nèi)存管理機(jī)制中講述了java運(yùn)行時(shí)區(qū)域的各個(gè)部分妥箕,其中程序計(jì)數(shù)器、虛擬機(jī)棧更舞、本地方法棧3個(gè)區(qū)域隨線程而生畦幢,隨線程而...
    Gelato_閱讀 292評(píng)論 0 0
  • 一、垃圾收集器 1.如何確定對(duì)象已死 1.1.引用計(jì)數(shù)法-Reference Counting 給對(duì)象添加一個(gè)引用...
    Jdqm閱讀 516評(píng)論 0 3
  • 本文主要思考三個(gè)問(wèn)題缆蝉。哪些內(nèi)存需要回收宇葱?什么時(shí)候回收瘦真?如何回收? 對(duì)象已死么黍瞧? 1.引用計(jì)數(shù)算法 給對(duì)象中添加一個(gè)...
    raincoffee閱讀 242評(píng)論 0 2