Java中JVM的雙親委派、內存溢出、垃圾回收和調優(yōu)詳解
1.雙親委派
1.1 類加載過程
【加載】:加載是指將類的字節(jié)碼文件讀入內存,并在內存中創(chuàng)建一個Class對象,用來描述該類的結構信息。類的字節(jié)碼可以來自本地磁盤、網絡等各種來源。
【連接】:連接是指對類的字節(jié)碼進行驗證、準備和解析的過程。
- 驗證:驗證字節(jié)碼文件的正確性和安全性。例如,驗證類的繼承關系是否合法、方法調用是否正確等。
- 準備:為類的靜態(tài)變量分配內存空間,并設置初始值。這些變量在該類的所有實例中共享。
- 解析:將類中的符號引用轉換為直接引用。符號引用是一種用來描述所引用的目標的符號,例如類、字段、方法等。直接引用是指指向具體內存地址的指針。
【初始化】:初始化是類加載過程的最后一步,主要是對類的靜態(tài)變量賦值和執(zhí)行靜態(tài)代碼塊。
- 執(zhí)行靜態(tài)代碼塊和靜態(tài)變量初始化器。
- 調用父類的初始化方法。
- 執(zhí)行類中定義的初始化方法。
1.2 類加載器
類加載器:是Java虛擬機(JVM)的一個重要組成部分,它的主要作用是將類的字節(jié)碼加載到內存中,并生成對應的Class對象。
System.setProperty("java.system.class.loader", "com.example.MyClassLoader");類加載器分類:
(1)第一種是 c++ 語言寫的 啟動類加載器:Bootstrap ClassLoader。
(2)第二種是 java 語言寫的 其他各種類加載器??梢苑譃閿U展類加載器, 應用程序類加載器 以及各種各樣的自定義類加載器。自定義類加載器 一般是需要進行動態(tài)加載或者其他業(yè)務目的類加載器。

- 啟動類加載器:它是JVM的一部分,主要負責加載Java的核心類庫,比如java.lang包中的類。
- 擴展類加載器:它負責加載JVM擴展目錄(java.ext.dirs)中的類庫,比如javax包中的類。
- 應用程序類加載器:它負責加載應用程序classpath目錄下的類庫,是程序中默認的類加載器。
- 自定義類加載器:它是開發(fā)人員自己實現(xiàn)的類加載器,可以根據自己的需求實現(xiàn)不同的加載邏輯。
1.3 雙親委派機制
【類的唯一性】在一個虛擬機里,一個類只有一個對應的類對象。 現(xiàn)在對類的唯一性的理解,應該更為準確地描述為: 在一個加載器下,一個類只有一個對應的類對象。如果有多個類加載器都各自加載了同一個類,那么他們將得到不同的類對象。
【雙親委派機制】JVM中的雙親委派機制是指,當一個類被加載時,JVM會首先委托其父類加載器去加載該類,只有當父類加載器無法加載該類時,才會由子類加載器去加載。這樣可以保證Java虛擬機中的類不會出現(xiàn)同名的情況,也能保證Java核心類庫的安全性和穩(wěn)定性。
- 通過覆蓋ClassLoader的findClass()方法,在該方法中定義特殊的類搜索方式或者直接拋出ClassNotFoundException異常來繞過雙親委派機制。
- 自定義類加載器并打破雙親委派機制,可以使用URLClassLoader類或者繼承ClassLoader類并重寫loadClass()方法。
需要注意的是,在使用類加載器打破雙親委派機制時,應該保證加載的類與父加載器加載的類之間沒有依賴關系,否則可能會導致異?;蛘叱霈F(xiàn)未知的錯誤。
(1)雙親委派機制的意義主要是保護一些基本類不受影響。比如常用的 String類, 其全限定名是 java.lang.String,只是 java.lang 這個包下的類在使用的時候,可以不用 import 而直接使用。像這種基本類按照雙親委派機制 都應該從 rt.jar 里去獲取,而不應該從自定義加載器里去獲取某個開發(fā)人員自己寫的 java.lang.String,畢竟開發(fā)人員自己寫的 java.lang.String 可能有很多 bug,通過這種方式,無論如何大家使用的都是 rt.jar 里的 java.lang.String 類了。
(2)避免類的重復加載。由于每個類加載器都有自己的命名空間,當父類加載器已經加載了一個類時,子類加載器再去加載該類時,就會導致重復加載,浪費內存空間。通過雙親委派機制,可以避免重復加載,提高JVM的運行效率。
(3)確保類的一致性。由于父類加載器的優(yōu)先級高于子類加載器,所以子類加載器無法覆蓋父類加載器已經加載的類。這樣可以保證不同的類加載器加載的同一個類是一致的,避免了由于不同版本的類加載器加載同一個類而導致的問題。
【注意】雙親委派機制對應的英文是:Parents Delegation Model,這個 Parents 后面有個 s,所以就被翻譯成雙親了。而 parents 英文本身想表達的意思是上溯,父輩,祖先的意思,即爸爸,爺爺,祖爺爺~但是卻被翻譯成雙親,雙親在中文里強調的是父親和母親兩個。所以是翻譯不夠準確,略微會引起一些歧義。
雖然雙親委派機制可以避免類的重復加載、提高JVM的運行效率和系統(tǒng)的穩(wěn)定性,但在一些特定的場景下,也需要打破雙親委派機制,例如:
- 實現(xiàn)類隔離。在某些情況下,需要使用不同的類加載器加載同一個類的不同版本,或者需要使用不同的類加載器加載同一個類的不同實現(xiàn),這時就需要打破雙親委派機制,實現(xiàn)類的隔離。
- 自定義類加載器。在某些情況下,需要使用自定義的類加載器加載類,例如在OSGi等動態(tài)模塊化框架中就需要使用自定義的類加載器。此時也需要打破雙親委派機制。
- 熱部署。在熱部署的場景下,需要動態(tài)更新已經加載的類,而雙親委派機制會阻止子類加載器重新加載已經由父類加載器加載的類,因此需要打破雙親委派機制,實現(xiàn)熱部署功能。
1.4 自定義類加載器
為什么需要類加載器:每個 Web 應用各自都需要配置一個專有的類加載器。Tomcat會有多個不同的war包,需要實現(xiàn)類隔離。
- 創(chuàng)建一個類 CustomizedClassLoader ,繼承自 ClassLoader。
- 定義屬性 classesFolder 其值是當前項目下的 classes_4_test 目錄。
- 重寫 loadClassData 方法,它會把傳進去的全限定類名,匹配到文件。
- 重寫 findClass 方法,把這個字節(jié)數組通過調用 defineClass 方法,就轉換成 HOW2J 這個類對應的 Class 對象了。
- 拿到這個類對象之后,通過反射機制,調用其 hello 方法,就能看到如圖所示的字符串:"Hello!"
import java.io.File;
import java.lang.reflect.Method;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
public class CustomizedClassLoader extends ClassLoader {
private File classesFolder = new File(System.getProperty("user.dir"),"classes_4_test");
protected Class<?> findClass(String QualifiedName) throws ClassNotFoundException {
byte[] data = loadClassData(QualifiedName);
return defineClass(QualifiedName, data, 0, data.length);
}
private byte[] loadClassData(String fullQualifiedName) throws ClassNotFoundException {
String fileName = StrUtil.replace(fullQualifiedName, ".", "/") + ".class";
File classFile = new File(classesFolder, fileName);
if(!classFile.exists())
throw new ClassNotFoundException(fullQualifiedName);
return FileUtil.readBytes(classFile);
}
public static void main(String[] args) throws Exception {
CustomizedClassLoader loader = new CustomizedClassLoader();
Class<?> how2jClass = loader.loadClass("cn.ysy.diytomcat.Test");
Object o = how2jClass.newInstance();
Method m = how2jClass.getMethod("hello");
m.invoke(o);
System.out.println(how2jClass.getClassLoader());
}
}2. 內存溢出&內存泄露
2.1 內存模型
【基本定義】Java的內存模型定義了Java程序在運行時的內存結構以及多線程情況下,多個線程之間如何共享內存。Java的內存模型保證了線程安全性,避免了多線程訪問共享內存時出現(xiàn)的數據競爭、死鎖等問題。
【組成部分】Java內存模型將內存分為兩個部分:線程工作內存和主內存。線程工作內存是線程獨有的內存空間,用于存儲線程運行時的局部變量等數據,而主內存是所有線程共享的內存空間,用于存儲Java程序中定義的全局變量等數據。
【特性】Java內存模型定義了一組規(guī)則,確保多個線程之間對共享內存的訪問是正確的。其中包括:
- 可見性:當一個線程修改了共享變量的值后,其他線程可以立即看到該變量的修改。
- 原子性:對共享變量的讀寫操作應該被視為一個原子操作,不可被中斷。
- 有序性:線程之間的操作可能會被編譯器、處理器進行指令重排等優(yōu)化,但是Java內存模型保證了操作執(zhí)行的順序不會影響程序的正確性。
2.2 內存溢出(OOM)
內存溢出就是指程序運行過程中申請的內存大于系統(tǒng)能夠提供的內存,導致無法申請到足夠的內存,于是就發(fā)生了內存溢出。
內存溢出原因(案例,場景):
- 內存中加載的數據量過于龐大,比如sql全表掃描 ;
- 集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;
- 代碼中存在死循環(huán)或循環(huán)產生過多重復的對象實體;
- 啟動參數內存值設定的過小。
- 堆棧溢出
解決內存溢出:
- 修改JVM啟動參數,直接增加內存。
- 檢查錯誤日志,查看“OutOfMemory”錯誤前是否有其它異常或錯誤。
- 對代碼進行走查和分析,找出可能發(fā)生內存溢出的位置。
- 使用內存查看工具動態(tài)查看內存使用情況。
堆棧溢出:
- Java堆溢出 Java堆用于儲存對象實例,我們只要不斷地創(chuàng)建對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么隨著對象數量的增加,總容量觸及最大堆的容量限制后就會產生內存溢出異常。
- 虛擬機棧和本地方法棧溢出 HotSpot虛擬機中并不區(qū)分虛擬機棧和本地方法棧,如果虛擬機的棧內存允許動態(tài)擴展,當擴展棧容量無法申請到足夠的內存時,將拋出OutOfMemoryError異常。
- 方法區(qū)和運行時常量池溢出 方法區(qū)溢出也是一種常見的內存溢出異常,在經常運行時生成大量動態(tài)類的應用場景里,就應該特別關注這些類的回收狀況。
- 本地直接內存溢出 直接內存的容量大小可通過`-XX:MaxDirectMemorySize`參數來指定,如果不去指定,則默認與Java堆最大值一致。如果直接通過反射獲取Unsafe實例進行內存分配,并超出了上述的限制時,將會引發(fā)OOM異常。
2.3 內存泄露
內存泄漏是指不再使用的對象仍然被引用,導致垃圾收集器無法回收它們的內存。由于不再使用的對象仍然無法清理,甚至這種情況可能會越積越多,最終導致致命的OutOfMemoryError。
內存泄漏場景:
- 動態(tài)分配內存但沒有及時釋放:如果程序中使用了動態(tài)內存分配(例如使用 malloc 函數),但沒有及時釋放內存(例如使用 free 函數),就會導致內存泄漏。
- 循環(huán)引用:如果程序中存在循環(huán)引用(例如兩個對象互相引用),就會導致這些對象無法被垃圾回收器回收,最終導致內存泄漏。
- 靜態(tài)變量未釋放:如果程序中存在靜態(tài)變量,且這些靜態(tài)變量在程序運行過程中未被釋放,就會導致內存泄漏。
- 沒有正確使用引用計數:如果程序中使用了引用計數,但沒有正確地使用引用計數,就會導致內存泄漏。
- 文件描述符未關閉:如果程序中使用了文件描述符(例如打開文件),但沒有正確關閉文件,就會導致內存泄漏。
內存泄露的原因:
- 長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收。
- 不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化后將在JVM的整個生命周期中存在(以靜態(tài)變量的方式),如果單例對象持有外部對象的引用,那么這個外部對象將不能被jvm正?;厥?,導致內存泄露 。
解決內存泄漏:
- 啟用分析器 Java分析器是通過應用程序監(jiān)視和診斷內存泄漏的工具,它可以分析我們的應用程序內部發(fā)生的事情,例如如何分配內存。使用分析器,我們可以比較不同的方法并找到可以最佳利用資源的方式。
- 啟用詳細垃圾收集日志 通過啟用詳細垃圾收集日志,我們可以跟蹤GC的詳細進度。要啟用該功能,我們需要將以下內容添加到JVM的配置當中:`-verbose:gc`。通過這個參數,我們可以看到GC內部發(fā)生的細節(jié)。
- 使用引用對象我們還可以借助java.lang.ref包內置的Java引用對象來規(guī)避問題,使用java.lang.ref包,而不是直接引用對象,即使用對象的特殊引用,使得它們可以輕松地被垃圾收集。
- Eclipse內存泄漏警告 對于JDK1.5以及更高的版本中,Eclipse會在遇到明顯的內存泄漏情況時顯示警告和錯誤。因此,在Eclipse中開發(fā)時,我們可以定期地訪問“問題”選項卡,并更加警惕內存泄漏警告。
3.垃圾回收機制
3.1 傳統(tǒng)垃圾回收
垃圾回收(Garbage Collection,GC)就是釋放垃圾占用的內存空間,對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,防止內存泄露。
【垃圾判斷算法】
- 引用計數法:給每個對象添加一個計數器,當有地方引用該對象時計數器加1,當引用失效時計數器減1。用對象計數器是否為0來判斷對象是否可被回收。
- 可達性分析:通過GC ROOT的對象作為搜索起始點,通過引用向下搜索,走過的路徑稱為引用鏈。通過對象是否到達引用鏈的路徑來判斷對象是否可被回收。
【垃圾回收算法】
- 標記-清除算法:標記清除算法(Mark-Sweep)是最基礎的一種垃圾回收算法,它分為2部分,先把內存區(qū)域中的這些對象進行標記,哪些屬于可回收標記出來,然后把這些垃圾拎出來清理掉。清理掉的垃圾就變成未使用的內存區(qū)域,等待被再次使用。
- 標記-復制算法:復制算法(Copying)是在標記清除算法基礎上演化而來,解決標記清除算法的內存碎片問題。它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。
- 標記-整理算法:標記-整理算法標記過程仍然與標記-清除算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,再清理掉端邊界以外的內存區(qū)域。
- 分代收集算法:分代收集算法分代收集算法嚴格來說并不是一種思想或理論,而是融合上述3種基礎的算法思想,而產生的針對不同情況所采用不同算法的一套組合拳,根據對象存活周期的不同將內存劃分為幾塊。
【Full GC&Minor GC】
- Minor GC:回收新生代,因為新生代對象存活時間很短,因此
Minor GC會頻繁執(zhí)行,執(zhí)行的速度一般也會比較快。 - Full GC:回收老年代和新生代,老年代的對象存活時間長,因此
Full GC很少執(zhí)行,執(zhí)行速度會比Minor GC慢很多。
【Full GC觸發(fā)條件】
- 調用 System.gc():只是建議虛擬機執(zhí)行 Full GC,但是虛擬機不一定真正去執(zhí)行。不建議使用這種方式,而是讓虛擬機管理內存。
- 老年代空間不足:大對象直接進入老年代、長期存活的對象進入老年代等。為了避免以上原因引起的 Full GC,應當盡量不要創(chuàng)建過大的對象以及數組、注意編碼規(guī)范避免內存泄露。
- 空間分配擔保失?。菏褂脧椭扑惴ǖ?Minor GC 需要老年代的內存空間作擔保,如果擔保失敗會執(zhí)行一次 Full GC。
- JDK 1.7 及以前的永久代空間不足:在 JDK 1.7 及以前,HotSpot 虛擬機中的方法區(qū)是用永久代實現(xiàn)的,永久代中存放的為一些 Class 的信息、常量、靜態(tài)變量等數據。當系統(tǒng)中要加載的類、反射的類和調用的方法較多時,永久代可能會被占滿,在未配置為采用 CMS GC 的情況下也會執(zhí)行 Full GC。
3.2 G1垃圾回收器
Garbage First(G1)收集器開創(chuàng)了收集器面向局部收集的設計思路和基于Region的內存布局形式。其他收集器的垃圾收集的目標范圍要么是整個新生代,要么就是整個老年代,再要么就是整個Java堆(Full GC)。而G1跳出了這個限制,G1垃圾回收器相較于其他垃圾回收器的一大特點就是它支持將Java堆內存劃分為多個大小相等的區(qū)域,每個區(qū)域能夠獨立管理,并且可以根據應用程序的實時需求動態(tài)地選擇哪些區(qū)域進行垃圾回收。
G1收集器的運作過程大致可劃分為以下四個步驟:初始標記、并發(fā)標記、最終標記、篩選回收。其中,初始標記和最終標記階段仍然需要停頓所有的線程,但是耗時很短。
- 初始標記階段:首先,G1會標記出從根對象直接可達的對象,這個過程是在年輕代暫停執(zhí)行的。這個階段結束后,G1就已經確定了那些對象需要被回收。
- 并發(fā)標記階段:這個階段是并發(fā)的,即應用程序繼續(xù)執(zhí)行,垃圾回收器并行標記那些不重要的對象,包括那些從根對象不可達但是與可達對象之間有引用關系的對象。
- 最終標記階段:這個階段是在并發(fā)標記階段完成后暫停執(zhí)行的,G1會重新掃描所有新添加的引用并標記它們,防止被回收。
- 篩選階段:在這個階段中,G1會確定需要被回收的區(qū)域,并開始將這些區(qū)域的存活對象復制到空閑的區(qū)域中,同時清理掉這些區(qū)域的未使用對象。
- 清理階段:在這個階段中,G1已經有一些可用的區(qū)域,在這個階段中將會對這些區(qū)域進行整理,并且更新所有的引用地址,使得GC的過程能夠再次啟動。
從JDK 9開始,G1成為了默認的垃圾回收器,它是HotSpot在JVM上推出的垃圾回收器,旨在取代CMS。G1的主要特點包括:
- 分區(qū):G1將堆內存劃分為多個大小相等的區(qū)域,每個區(qū)域的大小通常為1-32MB。
- 并行收集:G1采用多線程并行收集的方式,可以明顯縮短垃圾回收時間。
- 智能壓縮:G1生成一個可復制的新對象,而不是壓縮整個堆,這樣可以明顯降低垃圾回收的時間和開銷。
- 可預測的停頓時間:G1通過控制每個分區(qū)內的垃圾回收時間,可以預測垃圾回收時間,并對應用程序暫停時間進行優(yōu)化。
3.3 CMS垃圾回收器
CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,是基于標記清除算法實現(xiàn)的。它最主要的優(yōu)點是:并發(fā)收集、低停頓。它的運作過程相對于前面幾種收集器來說要更復雜一些,整個過程分為四個步驟,包括:初始標記、并發(fā)標記、重新標記、并發(fā)清除。
- 初始標記僅僅只是標記一下GC Roots能直接關聯(lián)到的對象,速度很快。
- 并發(fā)標記階段就是從GC Roots的直接關聯(lián)對象開始遍歷整個對象圖的過程,這個過程耗時較長但是不需要停頓用戶線程,可以與垃圾收集線程一起并發(fā)運行。
- 重新標記階段則是為了修正并發(fā)標記期間,因用戶程序繼續(xù)運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但也遠比并發(fā)標記階段的時間短。
- 并發(fā)清除階段,清理刪除掉標記階段判斷的已經死亡的對象,由于不需要移動存活對象,所以這個階段也是可以與用戶線程同時并發(fā)的。
CMS收集器是HotSpot虛擬機追求低停頓的第一次成功嘗試,但是它還遠達不到完美的程度,至少有以下三個明顯的缺點:
- 并發(fā)階段,它雖然不會導致用戶線程停頓,卻因為占用一部分線程而導致應用程序變慢,降低總吞吐量。
- 它無法處理“浮動垃圾”,有可能會出現(xiàn)“并發(fā)失敗”進而導致另一次Full GC的發(fā)生。
- 它是一款基于標記清除算法實現(xiàn)的收集器,這意味著收集結束時會有大量空間碎片產生。
G1與CMS的對比:G1從整體來看是基于標記整理算法實現(xiàn)的收集器,但從局部上看又是基于標記復制算法實現(xiàn)。無論如何,這兩種算法都意味著G1運作期間不會產生內存空間碎片,垃圾收集完成之后能提供規(guī)整的可用內存。比起CMS,G1的弱項也可以列舉出不少。例如在用戶程序運行過程中,G1無論是為了垃圾收集產生的內存占用還是程序運行時的額外執(zhí)行負載都要比CMS要高。
- 垃圾回收方式:G1使用基于區(qū)域(Region)的分代回收策略,而CMS則使用基于標記-清除(Mark-Sweep)算法。G1將堆內存劃分為多個大小相等的區(qū)域,并可通過動態(tài)調整回收集的大小來控制回收時間;CMS則主要集中在老年代的增量式垃圾回收。
- 回收效果:G1的整體性能優(yōu)于CMS,特別是在大內存、多核處理能力和更好的預測性上。G1可以更好地控制暫停時間,并減少因大型堆內存垃圾回收引起的應用程序暫停。CMS適用于小型到中型堆內存垃圾回收,不能很好地擴展到大型堆內存的情況。
- 對CPU和內存的影響:G1會消耗更多的CPU資源,在回收過程中會產生一些額外的開銷,但可以避免長時間的STW(Stop-The-World)現(xiàn)象。CMS在回收過程中也可能導致應用程序暫停,但占用的CPU資源相對較少。
- 內存分配方式:G1采用了隨機空間分配策略,而CMS則使用先進先出空間分配策略。
- G1與CMS的選擇:目前在小內存應用上CMS的表現(xiàn)大概率仍然要會優(yōu)于G1,而在大內存應用上G1則大多能發(fā)揮其優(yōu)勢,這個優(yōu)劣勢的Java堆容量平衡點通常在6GB至8GB之間。
3.4 ZGC垃圾回收
ZGC(Z Garbage Collector)是一個可伸縮性非常好的低延遲垃圾回收器,它在JDK 11中引入,旨在處理大型堆,適用于云部署和其他內存密集型應用程序。
ZGC的主要特點包括:
- 并發(fā)處理:ZGC在垃圾回收時,不會像傳統(tǒng)的GC一樣暫停整個應用程序,而是采用了并發(fā)處理的方式,讓應用程序和垃圾回收器同時運行。
- 基于Region的內存管理:ZGC將內存劃分為許多小的Region,每個Region都有自己的內存池和垃圾回收器線程。這種方式可以使得ZGC只回收少量的內存,從而減少GC停頓時間。
- 無需壓縮:ZGC不需要對內存進行壓縮,因為它采用了一種稱為“內存重映射”的技術,將不再使用的內存區(qū)域映射到一個新的虛擬地址空間中,從而釋放出物理內存。
ZGC具有高性能、低延遲、可預測和背壓感知等特點,適用于多核CPU的應用場景,它可以在不犧牲吞吐量的情況下,大大減少GC停頓時間,從而提高應用程序的響應速度和性能。
4.JVM調優(yōu)
4.1 JVM常用參數
- -Xmx:指定 Java 堆的最大內存容量,例如 -Xmx2g 表示最大可用內存為 2 GB。
- -Xms:指定 Java 堆的初始內存容量,例如 -Xms512m 表示初始可用內存為 512 MB。
- -Xmn:指定新生代的初始大小。
- -XX:MaxPermSize:指定非堆區(qū)的最大內存容量,例如 -XX:MaxPermSize=256m 表示最大可用內存為 256 MB。
- -XX:PermSize:指定非堆區(qū)的初始內存容量,例如 -XX:PermSize=128m 表示初始可用內存為 128 MB。
- -Xss:指定線程的堆棧大小,例如 -Xss1m 表示線程的堆棧大小為 1 MB。
- -XX:+UseParallelGC:使用并行垃圾回收器,提高回收效率。
- -XX:+UseConcMarkSweepGC:使用 CMS 垃圾回收器,在減少垃圾回收停頓時間方面表現(xiàn)出色。
- -XX:+PrintGCDetails:打印垃圾回收的詳細信息。
- -XX:+HeapDumpOnOutOfMemoryError:當發(fā)生 OutOfMemoryError 錯誤時,自動生成堆轉儲快照文件。
4.2 JVM調優(yōu)策略
- 定位問題:首先需要確定 JVM 調優(yōu)的目標和調優(yōu)范圍,以及具體的性能問題,例如內存使用過高、垃圾回收頻繁、響應時間慢等。
- 收集數據:通過 JVM 的監(jiān)控工具或第三方工具(例如 VisualVM、JConsole、JMC 等),收集 JVM 運行時的各項指標數據,例如 CPU 使用率、內存使用情況、垃圾回收信息等。
- 分析數據:分析收集到的數據,找出瓶頸所在,例如內存泄漏、垃圾回收效率低等問題,確定需要調整的 JVM 參數。
- 調整 JVM 參數:根據問題的根源,調整相應的 JVM 參數。例如,如果堆內存占用過高,可以增加堆內存的大?。ㄍㄟ^ -Xmx 參數)或者調整垃圾回收器的類型(通過 -XX:+UseConcMarkSweepGC 參數等)。
- 測試驗證:在實際環(huán)境中測試優(yōu)化后的 JVM,驗證是否能夠有效地解決問題,并進行持續(xù)性能監(jiān)測和優(yōu)化。
到此這篇關于Java中JVM的雙親委派、內存溢出、垃圾回收和調優(yōu)詳解的文章就介紹到這了,更多相關JVM的雙親委派、內存溢出內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
數組實現(xiàn)Java 自定義Queue隊列及應用操作
這篇文章主要介紹了數組實現(xiàn)Java 自定義Queue隊列及應用操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
springboot 項目容器啟動后如何自動執(zhí)行指定方法
這篇文章主要介紹了springboot 項目容器啟動后如何自動執(zhí)行指定方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
SpringBoot @Cacheable自定義KeyGenerator方式
這篇文章主要介紹了SpringBoot @Cacheable自定義KeyGenerator方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot中動態(tài)更新@Value配置方式
這篇文章主要介紹了SpringBoot中動態(tài)更新@Value配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
SpringCloudAlibaba整合Feign實現(xiàn)遠程HTTP調用的簡單示例
這篇文章主要介紹了SpringCloudAlibaba 整合 Feign 實現(xiàn)遠程 HTTP 調用,文章中使用的是OpenFeign,是Spring社區(qū)開發(fā)的組件,需要的朋友可以參考下2021-09-09

