JVM 內(nèi)存模型
JVM 整體結(jié)構(gòu)及內(nèi)存模型
JVM 內(nèi)存模型圖涉及變量代碼如下:
class Test {
public static Order order = new Order();
public int test() {
int a = 1;
int b = 2;
User user = new User();
int c = a + b;
return c;
}
public static void main(String[] args) {
new Test().test();
System.out.println("===test方法出口返回===");
}
}
class User {
}
class Order {
}
JVM 為為每個線程分配了獨立的椢裳。空間道逗,程序計數(shù)器,本地方法棧卖词。在一個線程內(nèi),方法的調(diào)用會給方法分配一塊棧幀即横,每個棧幀都有獨立的局部變量表裆赵,操作數(shù)棧,動態(tài)鏈接页藻,方法出口(保證了方法內(nèi)部局部變量的作用范圍)
test 方法操作數(shù)棧變化:在局部變量表中分配給變量 a 內(nèi)存空間植兰,將整數(shù) 1 壓入操作數(shù)棧,然后出棧賦值給變量 a楣导,在給變量 b 分配內(nèi)存空間,將整數(shù) 2 壓入操作數(shù)棧,然后出棧賦值給變量 b膝晾,從局部變量表中找到 a 和 b 的值务冕,存入操作數(shù)棧,進行整數(shù) add 操作臊旭,得到結(jié)果壓入操作數(shù)棧箩退,出棧賦值給變量 c,從局部變量表得到 c 的值滋戳,壓入操作數(shù)棧啥刻,執(zhí)行 return 返回。
GC 垃圾回收機制
Eden 區(qū)滿了之后會執(zhí)行 minor GC 清理 Eden 區(qū)和非空的 Survivor 區(qū)娄涩,GC 之后將存活對象移至空的 Survivor 區(qū),并將分代年齡+1映跟,分代年齡到 15 之后會將存活對象存入年老代蓄拣,當年老代滿了之后會執(zhí)行 Full GC 清理年輕代,年老代和元空間扬虚,如果 Full GC 之后沒有清理出空間就會出現(xiàn) OOM(內(nèi)存溢出)。 JVM 會根據(jù) GC Roots 查找所有依賴的對象直到最后一個沒有依賴的對象為止弯蚜,將這些對象標記為非垃圾對象孔轴。每次 GC 會伴隨 STW 機制。
什么是 GC Roots
JVM 中判斷一個對象是否標記為可回收的對象是根據(jù)可達性分析算法碎捺,顧名思義路鹰,可達性分析需要知道當前對象(是否需要回收的對象)的起點,而這個起點對象在當前時刻一定是存活的收厨,才能保證對當前對象是否需要回收的判斷是正確的晋柱,所以 GC Root 表示:當前時刻存活的對象诵叁。
GC Roots 包含以下幾種:
- 方法中的局部變量
- 靜態(tài)變量引用的對象
- 常量(被 final 修飾)
- 本地方法引用的對象
什么是 STW
Stop 一 the 一 World雁竞,簡稱 STW,指的是 Gc 事件發(fā)生過程中拧额,會使得用戶線程停止執(zhí)行碑诉,產(chǎn)生應用程序的卡頓。
JVM 為什么會設(shè)計 STW 機制
在 GC 期間侥锦,如果用戶線程不停止进栽,可能剛剛根據(jù) GC Roots 找到的非垃圾對象可能變成垃圾對象,使得 GC 未能回收恭垦。
JVM 內(nèi)存參數(shù)設(shè)置
Spring Boot 程序的 JVM 參數(shù)設(shè)置格式(Tomcat 啟動直接加在 bin 目錄下 catalina.sh 文件里)
‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
關(guān)于元空間的 JVM 參數(shù)有兩個:-XX:MetaspaceSize=N 和-XX:MaxMetaspaceSize=N
-XX:MaxMetaspaceSize:設(shè)置元空間最大值快毛,默認是-1,即不限制番挺,或者說只受限于本地內(nèi)存大小唠帝。
-XX:MetaspaceSize:指定元空間觸發(fā) Full GC 的初始閾值(元空間無固定初始大小),以字節(jié)為單位玄柏,默認是 21M襟衰,達到該值就會觸發(fā) Full GC 進行類型卸載,同時收集器會對該值進行調(diào)整:如果釋放了大量的空間粪摘,就適當降低該值右蒲;如果釋放了很少的空間,那么在不超過-XX:MaxMetaspaceSize(如果設(shè)置了的話)的情況下赶熟,
適當提高該值瑰妄。這個跟早期 jdk 版本的-XX:PermSize 參數(shù)意思不一樣,-XX:PermSize 代表永久代的初始容量映砖。由于調(diào)整元空間的大小需要 Full GC间坐,這是非常昂貴的操作,如果應用在啟動的時候發(fā)生大量 Full GC,通常都是由于永久代或元空間發(fā)生了大小調(diào)整竹宋,基于這種情況劳澄,一般建議在 JVM 參數(shù)中將 MetaspaceSize 和 MaxMetaspaceSize 設(shè)置成一樣的值,并設(shè)置得比初始值要大蜈七,對于 8G 物理內(nèi)存的機器來說秒拔,一般設(shè)置為 256M。
StackOverFlowError
public class TestStackOverFlow {
static int i = 0;
public static void overflow() {
i++;
overflow();
}
public static void main(String[] args) {
overflow();
}
}
結(jié)論:-Xss設(shè)置越小飒硅,說明一個線程棧里能分配的棧幀就越少砂缩,但是對JVM整體來說能開啟的線程數(shù)會更多。
JVM優(yōu)化就是盡可能讓對象都在新生代里分配和回收三娩,盡量別讓太多對象頻繁進入老年代庵芭,避免頻繁對老年代進行垃圾回收,同時給系統(tǒng)充足的內(nèi)存大小雀监,避免新生代頻繁的進行垃圾回收双吆。