前言
2023-06-19 18:49:33
出自B站 灰灰的Java面試
楓葉云鏈接:http://cloud.fynote.com/s/4976
JVM面試題大全 Lecturer :嚴鎮濤
1.為什么需要JVM,不要JVM可以嗎?
1.JVM可以幫助我們屏蔽底層的操作系統 一次編譯,到處運行
2.JVM可以運行Class文件
2.JDK,JRE以及JVM的關系
3.我們的編譯器到底干了什么事?
僅僅是將我們的 .java 文件轉換成了 .class 文件,實際上就是文件格式的轉換,對等信息轉換。
4.類加載機制
類加載機制其實就是虛擬機把Class文件加載到內存,并對數據進行校驗,轉換解析和初始化,形成可以虛擬機直接使用的Java類型,即java.lang.Class。
1.裝載
Class文件 – >二進制字節流 -->類加載器
1)通過一個類的全限定名獲取這個類的二進制字節流
2)將這個字節流所代表的靜態存儲結構轉換為方法區的運行時數據結構
3)在java堆中生成一個代表這個類的java.lang.Class對象,做為我們方法區的數據訪問入口
2.鏈接:
1)驗證:保證我們加載的類的正確性
- 文件格式驗證
- 元數據驗證
- 字節碼驗證
- 符號引用驗證
2)準備
為類的靜態變量分配內存,并將其初始化為當前類型的默認值。
private static int a = 1 ; 那么他在準備這個階段 a = 0;
3)解析
解析是從運行時常量池中的符號引用動態確定具體值的過程。
把類中的符號引用轉換成直接引用
3.初始化
執行到Clinit方法,為靜態變量賦值,初始化靜態代碼塊,初始化當前類的父類
5.類加載器的層次
6.雙親委派機制
父類委托機制
源碼 String 自己寫 String
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
7.如何打破雙親委派
1.復寫
2.SPI Service Provider Interface 服務提供接口 日志 Xml解析 JBDC
可拔插設計 可以隨時替換實現
3.OSGI 熱部署 熱更新
8.運行時數據區
1.方法區 線程共享 方法區是邏輯上堆的一部分 所以他有個名字:非堆
運行時常量池、字段和方法數據,以及方法和構造函數的代碼,包括類和實例初始化和接口初始化中使用 的特殊方法
如果方法區域中的內存無法滿足分配請求,Java 虛擬機將拋出一個 OutOfMemoryError
2.堆 線程共享 堆是為所有類實例和數組分配內存的運行時數據區域 內存不足 OutOfMemoryError
3.java虛擬機棧 執行java方法的 線程私有的 StackOverflowError
4.本地方法棧 執行本地方法 線程私有 StackOverflowError
5.程序計數器 記錄程序執行到的位置 線程私有
9.棧幀結構是什么樣子的?
附加信息:棧幀的高度,虛擬機版本信息
棧幀信息:附加信息+動態鏈接+方法的返回地址
局部變量表:方法中定義的局部變量以及方法的參數都會存放在這張表中 ,單純的存儲單元
操作數棧 以壓棧以及出棧的方式存儲操作數
舉例:
int a = 1;
int b = 1;
int c = a + b ;
方法的返回地址:當你一個方法執行的時候,只有兩種方式可以推出
1.遇到方法的返回的字節碼指令
2.出現了異常,有異常處理器,則交給異常處理器 ,沒有呢?拋異常
10.動態鏈接
動態鏈接是為了支持方法的動態調用過程 。
動態鏈接將這些符號方法引用轉換為具體的方法引用
符號引用轉變為直接引用 為了支持java的多態
void a(){
b();
}
void b(){
c();
}
void c(){
}
11.java堆為什么要進行分代設計
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uMBO7nGP-1687171635915)(file:///C:\Users\root\AppData\Local\Temp\ksohtml\wpsE144.tmp.jpg)]
新老年代劃分
Eden區與S區
12.[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NYW5rLRd-1687171635917)(file:///C:\Users\root\AppData\Local\Temp\ksohtml\wps7F2D.tmp.jpg)]老年代的擔保機制
13.為什么Eden:S0:S1 是8:1:1[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-s7CYE1t9-1687171635918)(file:///C:\Users\root\AppData\Local\Temp\ksohtml\wps942D.tmp.jpg)]
98%的對象朝生夕死
14.對象的創建過程
15.方法區與元數據區以及持久代到底是什么關系
方法區 JVM規范
落地:JDK1.7之前 持久代 Perm Space JVM虛擬機自己的內存
JDK1.8之后 元數據區 / 元空間 MetaSpace 直接內存
16.什么時候才會進行垃圾回收
GC是由JVM自動完成的,根據JVM系統環境而定,所以時機是不確定的。
當然,我們可以手動進行垃圾回收,比如調用System.gc()方法通知JVM進行一次垃圾回收,但是
具體什么時刻運行也無法控制。也就是說System.gc()只是通知要回收,什么時候回收由JVM決
定。但是不建議手動調用該方法,因為GC消耗的資源比較大。
(1)當Eden區或者S區不夠用了
(2)老年代空間不夠用了
(3)方法區空間不夠用了
(4)System.gc() //通知 時機也不確定 執行的Full GC
17. 如何確定一個對象是垃圾?
要想進行垃圾回收,得先知道什么樣的對象是垃圾。
- 引用計數法
對于某個對象而言,只要應用程序中持有該對象的引用,就說明該對象不是垃圾,如果一個對象沒有任何指針對其引用,它就是垃圾。
弊端
:如果AB相互持有引用,導致永遠不能被回收。 循環引用 內存泄露 -->內存溢出
- 可達性分析/根搜索算法
通過GC Root的對象,開始向下尋找,看某個對象是否可達
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Oee0Mcut-1687171635921)(file://E:/%E6%A1%8C%E9%9D%A2/yzt/%E7%AC%94%E8%AE%B0%E8%AF%BE%E4%BB%B6/JVM/3%E5%A4%A9JVM%E8%AE%AD%E7%BB%83%E8%90%A5/%E8%B5%84%E6%96%99+%E7%AC%94%E8%AE%B0/images/64.png?lastModify=1646659177)]
能作為GC Root:類加載器、Thread、虛擬機棧的本地變量表、static成員、常量引用、本地方法棧的變量等。
虛擬機棧(棧幀中的本地變量表)中引用的對象。
方法區中類靜態屬性引用的對象。
方法區中常量引用的對象。
本地方法棧中JNI(即一般說的Native方法)引用的對象。
18.對象被判定為不可達對象之后就“死”了嗎
垃圾收集算法
已經能夠確定一個對象為垃圾之后,接下來要考慮的就是回收,怎么回收呢?得要有對應的算法,下面介紹常見的垃圾回收算法。高效 健壯
標記-清除(Mark-Sweep)
- 標記
找出內存中需要回收的對象,并且把它們標記出來
此時堆中所有的對象都會被掃描一遍,從而才能確定需要回收的對象,比較耗時
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IuL0BB27-1687171635922)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\25.png?lastModify=1646720640)]
- 清除
清除掉被標記需要回收的對象,釋放出對應的內存空間
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-roEivuWw-1687171635924)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\26.png?lastModify=1646720640)]
缺點
標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能會導致以后在程
序運行過程中需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
(1)標記和清除兩個過程都比較耗時,效率不高
(2)會產生大量不連續的內存碎片,空間碎片太多可能會導致以后在程序運行過程中需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
標記-復制(Mark-Copying)
將內存劃分為兩塊相等的區域,每次只使用其中一塊,如下圖所示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pyg6A15O-1687171635925)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\27.png?lastModify=1646720640)]
當其中一塊內存使用完了,就將還存活的對象復制到另外一塊上面,然后把已經使用過的內存空間一次清除掉。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LTJ91xFA-1687171635926)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\28.png?lastModify=1646720640)]
缺點:
空間利用率降低。
標記-整理(Mark-Compact)
復制收集算法在對象存活率較高時就要進行較多的復制操作,效率將會變低。更關鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行分配擔保,以應對被使用的內存中所有對象都有100%存活的極端情況,所以老年代一般不能直接選用這種算法。
標記過程仍然與"標記-清除"算法一樣,但是后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存。
其實上述過程相對"復制算法"來講,少了一個"保留區"
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tnNGnHQX-1687171635928)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\25.png?lastModify=1646720640)]
讓所有存活的對象都向一端移動,清理掉邊界意外的內存。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UVCyLkZr-1687171635929)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\29.png?lastModify=1646720640)]
分代收集算法
既然上面介紹了3中垃圾收集算法,那么在堆內存中到底用哪一個呢?
Young區:復制算法(對象在被分配之后,可能生命周期比較短,Young區復制效率比較高)
Old區:標記清除或標記整理(Old區對象存活時間比較長,復制來復制去沒必要,不如做個標記再清理)
垃圾收集器
如果說收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體實現。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UaJ6u0MM-1687171635931)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\30.png?lastModify=1646736013)]
- Serial
Serial收集器是最基本、發展歷史最悠久的收集器,曾經(在JDK1.3.1之前)是虛擬機新生代收集的唯一選擇。
它是一種單線程收集器,不僅僅意味著它只會使用一個CPU或者一條收集線程去完成垃圾收集工作,更重要的是其在進行垃圾收集的時候需要暫停其他線程。
優點:簡單高效,擁有很高的單線程收集效率
缺點:收集過程需要暫停所有線程
算法:復制算法
適用范圍:新生代
應用:Client模式下的默認新生代收集器
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2RFO6GgQ-1687171635932)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\31.png?lastModify=1646736013)]
- Serial Old
Serial Old收集器是Serial收集器的老年代版本,也是一個單線程收集器,不同的是采用"標記-整理算法",運行過程和Serial收集器一樣。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-grvtJfYu-1687171635934)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\32.png?lastModify=1646736013)]
- ParNew
可以把這個收集器理解為Serial收集器的多線程版本。
優點:在多CPU時,比Serial效率高。
缺點:收集過程暫停所有應用程序線程,單CPU時比Serial效率差。
算法:復制算法
適用范圍:新生代
應用:運行在Server模式下的虛擬機中首選的新生代收集器
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yBlAzkgL-1687171635935)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\33.png?lastModify=1646736013)]
- Parallel Scavenge
Parallel Scavenge收集器是一個新生代收集器,它也是使用復制算法的收集器,又是并行的多線程收集器,看上去和ParNew一樣,但是Parallel Scanvenge更關注系統的吞吐量。
吞吐量=運行用戶代碼的時間/(運行用戶代碼的時間+垃圾收集時間)
比如虛擬機總共運行了100分鐘,垃圾收集時間用了1分鐘,吞吐量=(100-1)/100=99%。
若吞吐量越大,意味著垃圾收集的時間越短,則用戶代碼可以充分利用CPU資源,盡快完成程序的運算任務。
-XX:MaxGCPauseMillis控制最大的垃圾收集停頓時間,
-XX:GCRatio直接設置吞吐量的大小。
- Parallel Old
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和標記-整理算法進行垃圾回收,也是更加關注系統的吞吐量。
- CMS
官網
: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collectorCMS(Concurrent Mark Sweep)收集器是一種以獲取
最短回收停頓時間
為目標的收集器。采用的是"標記-清除算法",整個過程分為4步
(1)初始標記 CMS initial mark 標記GC Roots直接關聯對象,不用Tracing,速度很快
(2)并發標記 CMS concurrent mark 進行GC Roots Tracing
(3)重新標記 CMS remark 修改并發標記因用戶程序變動的內容
(4)并發清除 CMS concurrent sweep 清除不可達對象回收空間,同時有新垃圾產生,留著下次清理稱為浮動垃圾
由于整個過程中,并發標記和并發清除,收集器線程可以與用戶線程一起工作,所以總體上來說,CMS收集器的內存回收過程是與用戶線程一起并發地執行的。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JNAs0so6-1687171635936)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\34.png?lastModify=1646736013)]
優點:并發收集、低停頓
缺點:產生大量空間碎片、并發階段會降低吞吐量
- G1(Garbage-First)
官網
: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#garbage_first_garbage_collection**使用G1收集器時,Java堆的內存布局與就與其他收集器有很大差別,它將整個Java堆劃分為多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Region(不需要連續)的集合。 **
每個Region大小都是一樣的,可以是1M到32M之間的數值,但是必須保證是2的n次冪
如果對象太大,一個Region放不下[超過Region大小的50%],那么就會直接放到H中
設置Region大小:-XX:G1HeapRegionSize=M
所謂Garbage-Frist,其實就是優先回收垃圾最多的Region區域
(1)分代收集(仍然保留了分代的概念) (2)空間整合(整體上屬于“標記-整理”算法,不會導致空間碎片) (3)可預測的停頓(比CMS更先進的地方在于能讓使用者明確指定一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6wS8vB9c-1687171635937)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\35.png?lastModify=1646736013)]
工作過程可以分為如下幾步
初始標記(Initial Marking) 標記以下GC Roots能夠關聯的對象,并且修改TAMS的值,需要暫停用戶線程
并發標記(Concurrent Marking) 從GC Roots進行可達性分析,找出存活的對象,與用戶線程并發執行
最終標記(Final Marking) 修正在并發標記階段因為用戶程序的并發執行導致變動的數據,需暫停用戶線程
篩選回收(Live Data Counting and Evacuation) 對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間制定回收計劃
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FxjE87pX-1687171635938)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\36.png?lastModify=1646736013)]
- ZGC
官網
: https://docs.oracle.com/en/java/javase/11/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0JDK11新引入的ZGC收集器,不管是物理上還是邏輯上,ZGC中已經不存在新老年代的概念了
會分為一個個page,當進行GC操作時會對page進行壓縮,因此沒有碎片問題
只能在64位的linux上使用,目前用得還比較少
(1)可以達到10ms以內的停頓時間要求
(2)支持TB級別的內存
(3)堆內存變大后停頓時間還是在10ms以內
垃圾收集器分類
- 串行收集器->Serial和Serial Old
只能有一個垃圾回收線程執行,用戶線程暫停。
適用于內存比較小的嵌入式設備
。
- 并行收集器[吞吐量優先]->Parallel Scanvenge、Parallel Old、ParNeww
多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態。
適用于科學計算、后臺處理等若交互場景
。
- 并發收集器[停頓時間優先]->CMS、G1、ZGC
用戶線程和垃圾收集線程同時執行(但并不一定是并行的,可能是交替執行的),垃圾收集線程在執行的時候不會停頓用戶線程的運行。
適用于相對時間有要求的場景,比如Web
。
吞吐量和停頓時間
- **停頓時間->垃圾收集器 **
進行
垃圾回收終端應用執行響應的時間 - 吞吐量->運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)
停頓時間越短就越適合需要和用戶交互的程序,良好的響應速度能提升用戶體驗;
高吞吐量則可以高效地利用CPU時間,盡快完成程序的運算任務,主要適合在后臺運算而不需要太多交互的任務。
小結
:這兩個指標也是評價垃圾回收器好處的標準。
生產環境中,如何選擇合適的垃圾收集器
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref28
- 優先調整堆的大小讓服務器自己來選擇
- 如果內存小于100M,使用串行收集器
- 如果是單核,并且沒有停頓時間要求,使用串行或JVM自己選
- 如果允許停頓時間超過1秒,選擇并行或JVM自己選
- 如果響應時間最重要,并且不能超過1秒,使用并發收集器
如何判斷是否使用G1垃圾收集器
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases
JDK 7開始使用,JDK 8非常成熟,JDK 9默認的垃圾收集器,適用于新老生代。
是否使用G1收集器?
(1)50%以上的堆被存活對象占用
(2)對象分配和晉升的速度變化非常大
(3)垃圾回收時間比較長
JVM常用命令
- jps
查看java進程
The jps command lists the instrumented Java HotSpot VMs on the target system. The command is limited to reporting information on JVMs for which it has the access permissions.
- jinfo
(1)實時查看和調整JVM配置參數
The jinfo command prints Java configuration information for a specified Java process or core file or a remote debug server. The configuration information includes Java system properties and Java Virtual Machine (JVM) command-line flags.
(2)查看用法
jinfo -flag name PID 查看某個java進程的name屬性的值
jinfo -flag MaxHeapSize PID
jinfo -flag UseG1GC PID
3)修改
參數只有被標記為manageable的flags可以被實時修改
jinfo -flag [+|-] PID
jinfo -flag <name>=<value> PID
(4)查看曾經賦過值的一些參數
jinfo -flags PID
- jstat
(1)查看虛擬機性能統計信息
The jstat command displays performance statistics for an instrumented Java HotSpot VM. The target JVM is identified by its virtual machine identifier, or vmid option.
(2)查看類裝載信息
jstat -class PID 1000 10 查看某個java進程的類裝載信息,每1000毫秒輸出一次,共輸出10次
(3)查看垃圾收集信息
jstat -gc PID 1000 10
- jstack
(1)查看線程堆棧信息
The jstack command prints Java stack traces of Java threads for a specified Java process, core file, or remote debug server.
(2)用法
jstack PID
JVM死鎖情況分析
//運行主類
public class DeadLockDemo
{public static void main(String[] args){DeadLock d1=new DeadLock(true);DeadLock d2=new DeadLock(false);Thread t1=new Thread(d1);Thread t2=new Thread(d2);t1.start();t2.start();}
}
//定義鎖對象
class MyLock{public static Object obj1=new Object();public static Object obj2=new Object();
}
//死鎖代碼
class DeadLock implements Runnable{private boolean flag;DeadLock(boolean flag){this.flag=flag;}public void run() {if(flag) {while(true) {synchronized(MyLock.obj1) {System.out.println(Thread.currentThread().getName()+"----if獲得obj1鎖");synchronized(MyLock.obj2) {System.out.println(Thread.currentThread().getName()+"----if獲得obj2鎖");}}}}else {while(true){synchronized(MyLock.obj2) {System.out.println(Thread.currentThread().getName()+"----否則獲得obj2鎖");synchronized(MyLock.obj1) {System.out.println(Thread.currentThread().getName()+"----否則獲得obj1鎖");}}}}}
}
- jmap
(1)生成堆轉儲快照
The jmap command prints shared object memory maps or heap memory details of a specified process, core file, or remote debug server.
(2)打印出堆內存相關信息
jmap -heap PID
jinfo -flag UsePSAdaptiveSurvivorSizePolicy 35352
-XX:SurvivorRatio=8
G1調優策略
(1)不要手動設置新生代和老年代的大小,只要設置整個堆的大小
why:https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc
G1收集器在運行過程中,會自己調整新生代和老年代的大小 其實是通過adapt代的大小來調整對象晉升的速度和年齡,從而達到為收集器設置的暫停時間目標 如果手動設置了大小就意味著放棄了G1的自動調優
(2)不斷調優暫停時間目標
一般情況下這個值設置到100ms或者200ms都是可以的(不同情況下會不一樣),但如果設置成50ms就不太合理。暫停時間設置的太短,就會導致出現G1跟不上垃圾產生的速度。最終退化成Full GC。所以對這個參數的調優是一個持續的過程,逐步調整到最佳狀態。暫停時間只是一個目標,并不能總是得到滿足。
(3)使用-XX:ConcGCThreads=n來增加標記線程的數量
IHOP如果閥值設置過高,可能會遇到轉移失敗的風險,比如對象進行轉移時空間不足。如果閥值設置過低,就會使標記周期運行過于頻繁,并且有可能混合收集期回收不到空間。 IHOP值如果設置合理,但是在并發周期時間過長時,可以嘗試增加并發線程數,調高ConcGCThreads。
**(4)MixedGC調優 **
-XX:InitiatingHeapOccupancyPercent -XX:G1MixedGCLiveThresholdPercent -XX:G1MixedGCCountTarger -XX:G1OldCSetRegionThresholdPercent
(5)適當增加堆內存大小
(6)不正常的Full GC
有時候會發現系統剛剛啟動的時候,就會發生一次Full GC,但是老年代空間比較充足,一般是由Metaspace區域引起的。可以通過MetaspaceSize適當增加其大家,比如256M。
JVM性能優化指南
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8aDDSsGH-1687171635940)(file://E:\桌面\yzt\筆記課件\JVM\3天JVM訓練營\資料+筆記\images\60.png?lastModify=1646833365)]