為什么要學(xué)習(xí)JVM?
jvm和算法是java程序員的內(nèi)功
內(nèi)存與對(duì)象
1.Java內(nèi)存區(qū)域分布于概述
五個(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í)銷毀济竹。
- 所有線程共享一個(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ō)一下棧幀
局部變量表(Local Variable Array)
局部變量表用于存儲(chǔ)方法參數(shù)和方法內(nèi)部定義的局部變量册招。
它是一個(gè)數(shù)組岔激,索引從0開(kāi)始。對(duì)于實(shí)例方法是掰,索引為0的位置通常存儲(chǔ)this引用虑鼎。操作數(shù)棧(Operand Stack)
操作數(shù)棧用于字節(jié)碼指令的操作數(shù)臨時(shí)存儲(chǔ)。
在方法執(zhí)行過(guò)程中键痛,字節(jié)碼指令會(huì)將數(shù)據(jù)壓入或彈出操作數(shù)棧炫彩,進(jìn)行計(jì)算或操作。動(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)引用。方法出口(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í)常量池中存放。
- 并非數(shù)據(jù)進(jìn)入了方法區(qū)就如永久代的名字一樣“永久”存在了。
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)的
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)
-
對(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ì)象
直接指針訪問(wèn)對(duì)象
垃圾回收
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.
對(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)始,任何一條引用鏈能引用到的
- 根搜索算法是從離散數(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)
-
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)躏碳。
- 標(biāo)記階段
在標(biāo)記階段搞旭,垃圾回收器會(huì)遍歷所有可達(dá)對(duì)象,并將其標(biāo)記為“存活”菇绵。遍歷從根集合(Root Set)開(kāi)始肄渗,根集合通常包括棧中的引用、全局靜態(tài)變量和寄存器中的引用咬最。所有從根集合可以訪問(wèn)到的對(duì)象都被認(rèn)為是存活的翎嫡。
- 標(biāo)記階段
- 清除階段
在清除階段,垃圾回收器會(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
復(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)存到底是怎么分配
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ū)域:
- Eden區(qū):所有的新對(duì)象首先在Eden區(qū)分配內(nèi)存。
- 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è)示例中:
-
array1
和array2
對(duì)象首先分配在Eden區(qū)缩挑。 - 創(chuàng)建
array3
對(duì)象后,Eden區(qū)可能會(huì)滿鬓梅,觸發(fā)一次Minor GC供置,將存活的array1
和array2
復(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)定位