如何有效管理JVM中的垃圾?
前言
都說JVM是大牛們玩的技術(shù),其實(shí)未必,如果面試官和你你談到Java內(nèi)存管理,那么首先,我建議你首先要了解Java垃圾收集的工作原理。 因?yàn)榻?jīng)常在運(yùn)行JAVA應(yīng)用程序時(shí),大多數(shù)開發(fā)者是使用JVM自動幫你管理GC垃圾回收器(完全不關(guān)注,JVM自動完成回收),碼農(nóng)們只關(guān)注業(yè)務(wù)代碼實(shí)現(xiàn),不需要關(guān)注JVM是怎么管理的,對大家而言,更多人只知道程序正在運(yùn)行中。但是老鐵們,當(dāng)你寫的JAVA程序開始面臨性能下降時(shí),碼農(nóng)與架構(gòu)師的區(qū)別就來了,所有的性能問題其實(shí)歸根到底就是我們的GC回收效率變低了。
因此,讓我們首先了解什么是JVM GC模型,然后,我們可以看到如何控制它并分析GC日志以查找應(yīng)用程序中發(fā)生的任何差異。
一、什么是自動垃圾收集?
自動垃圾收集是指對堆內(nèi)存的查看,并識別哪些對象正在使用哪些對象,以及刪除未使用的對象的過程。
首先我們看一下自動GC垃圾收集,它的步驟如下:
1.標(biāo)記(Marking)
該過程的第一步稱為標(biāo)記。其實(shí)就是垃圾收集器識別哪些內(nèi)存正在使用,哪些內(nèi)存不在使用的地方。
如果必須掃描系統(tǒng)中的所有對象,將是一個(gè)非常耗時(shí)的過程。
2.正常刪除(Normal Deletion)
正常刪除是指移除未引用的對象,留下引用的對象和指向空閑空間的指針。
3.壓縮刪除(Deletion with Compacting)
要進(jìn)一步提高性能,除了刪除未引用的對象外,還可以壓縮剩余的引用對象。
通過將引用的對象移動到一起,這使得新的內(nèi)存分配更加容易和快速。
二、全自動回收管理
當(dāng)正在進(jìn)行垃圾收集時(shí),如果你的應(yīng)用程序在該時(shí)間段內(nèi)沒有響應(yīng)時(shí),其實(shí)我們的期望是,GC應(yīng)該花費(fèi)最少的時(shí)間來回收它; 當(dāng)然, 如果花費(fèi)很多時(shí)間,則證明你的應(yīng)用GC設(shè)置是有問題滴。
我們來看看下面的JVM內(nèi)存模型,它分為不同的部分。JVM堆內(nèi)存在物理上分為兩部分 - Young Generation(新生代)和Old Generation(老年代)。
1.首先,將所有新的對象都分配給伊Eden space(伊甸園)。兩個(gè)Survivor Space(幸存者區(qū))都是空的。
2.當(dāng)Eden space(伊甸園)填滿時(shí),會觸發(fā)一個(gè)小的垃圾收集。
3.引用的對象被移動到第一個(gè)幸存者空間。清除Eden space(伊甸園)時(shí),將刪除未引用的對象。
4.下次要GC回收時(shí),Eden space(伊甸園)空間也會發(fā)生同樣的事情。刪除未引用的對象,并將引用的對象移動到幸存者空間。但是,在這種情況下,它們被移動到第二個(gè)幸存者空間(S1)。
5.在較小的GC之后,當(dāng)老化的對象達(dá)到一定的年齡閾值(在該示例中為8)時(shí),它們從新生代晉升到老年代。
最終,將對老一代進(jìn)行主要的GC回收,清理和壓縮該空間。
三、如何在Java中調(diào)整垃圾收集的優(yōu)化參數(shù)呢?
垃圾收集是指當(dāng)JVM不再需要對象時(shí),需要將它回收,釋放內(nèi)存。它包括查找不再使用的對象,釋放與這些對象關(guān)聯(lián)的內(nèi)存,并偶爾壓縮堆以防止內(nèi)存碎片。
垃圾收集器使用一個(gè)或多個(gè)線程來執(zhí)行回收工作。一般來說,為了完成跟蹤對象引用及在內(nèi)存中移動對象的工作,它需要確保應(yīng)用程序線程當(dāng)前沒有使用這些對象,如果應(yīng)用程序線程正在使用對象,GC回收時(shí)會導(dǎo)致對象的內(nèi)存位置發(fā)生變化,可能發(fā)生不可預(yù)測的事情。這就是垃圾收集器在執(zhí)行某些任務(wù)時(shí)必須暫停所有應(yīng)用程序線程的原因。這些暫停有時(shí)被稱為Stop-The-World暫停(吊炸天,全世界都被停止,哈哈)。
3.1調(diào)整堆大小
垃圾收集調(diào)優(yōu)的第一步是**調(diào)整堆的大小。**這是因?yàn)槿绻烟?,則會發(fā)生太多的GC回收回收內(nèi)存次數(shù),這會降低整體應(yīng)用程序吞吐量。如果堆太大,那么GC回收次數(shù)會更少,但GC需要很長的時(shí)間,那么你的系統(tǒng)響應(yīng)時(shí)間指標(biāo)會受到影響。并行收集器特別容易受到堆大小的影響,因此如果你需要大的堆并且暫停時(shí)間較短,那么你應(yīng)該嘗試使用G1GC收集器。
**備注:**自從Java 9和Shenandoah垃圾收集器被視為還處于“實(shí)驗(yàn)性”階段,不推薦使用并發(fā)標(biāo)記掃描(CMS)收集器。但如果你正在運(yùn)行在線交互式應(yīng)用程序,那么系統(tǒng)會默認(rèn)選擇G1GC收集器,如果你正在運(yùn)行脫機(jī)批處理應(yīng)用程序,那么并行收集器應(yīng)該是你的首選,這是我給大家的建議。
**堆的大小由兩個(gè)值控制:**使用ms標(biāo)志指定的初始值和使用mx標(biāo)志指定的最大值。
-Xms1g -Xmx8g
堆的初始大小和最大大小,可以由JVM根據(jù)工作負(fù)載自動調(diào)整堆大小。如果JVM遇到內(nèi)存壓力并且觀察到GC執(zhí)行次數(shù)過多,它會不斷增加堆,直到內(nèi)存壓力消失為止,或直到堆達(dá)到其最大值為止。如果內(nèi)存壓力很低,JVM還可以通過縮小堆大小來決定減少暫停時(shí)間。這個(gè)過程稱為自適應(yīng)大小調(diào)整**,**它不僅可以調(diào)整堆的整體大小,還可以調(diào)整年輕代和老代的大小和比例。
當(dāng)然,如果你想調(diào)整GC行為和大小,**我建議你可以選擇關(guān)閉自適應(yīng)大小調(diào)整。**這可以節(jié)省JVM,這是計(jì)算堆大小所需的一小段時(shí)間。你可以通過將標(biāo)志設(shè)置UseAdaptiveSizePolicy為false 來執(zhí)行此操作。
-XX:-UseAdaptiveSizePolicy
此外,將初始堆大小設(shè)置為與最大堆大小相同的值,或?qū)⒊跏夹律笮≡O(shè)置為與最大新生代大小相同的值,這樣操作可以有效地關(guān)閉自適應(yīng)大小調(diào)整。
一般來說堆大小的最大設(shè)置準(zhǔn)則就是**最大堆大小不應(yīng)超過計(jì)算機(jī)上的物理內(nèi)存量。**如果你運(yùn)行多個(gè)JVM,則最大堆大小的總和不應(yīng)超過計(jì)算機(jī)的物理內(nèi)存。
3.2調(diào)整GC性能
在G1GC中,調(diào)整參數(shù)MaxGCPauseMillis執(zhí)行以下所有優(yōu)化,以嘗試實(shí)現(xiàn)指定的暫停時(shí)間目標(biāo):
- 調(diào)整堆的大小
- 更快開始后臺處理
- 調(diào)整要提升為舊一代的對象的期限閾值
- 調(diào)整混合GC循環(huán)期間處理的舊區(qū)域數(shù)
3.3修復(fù)并發(fā)模式失敗
**G1GC是一個(gè)并發(fā)收集器。**這意味著當(dāng)應(yīng)用程序線程仍在運(yùn)行時(shí),垃圾收集進(jìn)程的某些階段可以并發(fā)運(yùn)行。并且由于正在運(yùn)行的應(yīng)用程序可以繼續(xù)產(chǎn)生垃圾,我們可能會遇到應(yīng)用程序耗盡舊代內(nèi)存而垃圾收集器仍在垃圾收集過程中的情況。也就是說,正在運(yùn)行的應(yīng)用程序生成的垃圾比它清理的速度快。**這種情況稱為并發(fā)模式故障,**具體取決于故障發(fā)生的時(shí)間。如果您在GC日志中看到很多這些錯(cuò)誤; 解決方案是增加堆的大小,更早地啟動G1后臺處理,或者通過使用更多后臺線程來加速GC處理。
要更頻繁地執(zhí)行G1后臺活動,您可以降低觸發(fā)G1循環(huán)的閾值。這是通過減少InitiatingHeapOccupancyPercent標(biāo)志的值來實(shí)現(xiàn)的。
-XX:InitiatingHeapOccupancyPercent=45
默認(rèn)情況下,此標(biāo)志設(shè)置為45。這意味著**當(dāng)堆填充45%時(shí)會觸發(fā)GC循環(huán)。**減少此值意味著GC會更早且更頻繁地觸發(fā)。但應(yīng)注意的是,該值不會設(shè)置為太低而導(dǎo)致GC過于頻繁發(fā)生的數(shù)字。
要增加后臺線程數(shù),請使用該ConcGCThreads標(biāo)志。
-XX:ConcGCThreads=4
此標(biāo)志的默認(rèn)值設(shè)置為ParallelGCThreads加2 的值除以4.只要計(jì)算機(jī)上有足夠的CPU可用,就可以增加此值而不會導(dǎo)致任何性能損失。
四、總結(jié)
如果調(diào)整堆大小并調(diào)整收集器對你不起作用,**那么你可以嘗試另一個(gè)收集器。如果你仍然沒有取得好成績,那么你需要考慮調(diào)整應(yīng)用程序代碼本身的問題了,好了,寫了這么多,希望對大家有幫助。
到此這篇關(guān)于如何有效管理JVM中的垃圾?的文章就介紹到這了,更多相關(guān)JVM垃圾管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法
這篇文章主要介紹了淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09兩個(gè)小例子輕松搞懂 java 中遞歸與尾遞歸的優(yōu)化操作
這篇文章主要介紹了兩個(gè)小例子輕松搞懂 java 中遞歸與尾遞歸的優(yōu)化操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Java多線程局域網(wǎng)聊天室的實(shí)現(xiàn)
在學(xué)習(xí)了一個(gè)學(xué)期的java以后,搞了一個(gè)多線程的聊天室,熟悉了一下服務(wù)器和客戶機(jī)的操作。感興趣的小伙伴們可以參考一下2021-06-06spring?boot?使用?@Scheduled?注解和?TaskScheduler?接口實(shí)現(xiàn)定時(shí)任務(wù)
這篇文章主要介紹了spring?boot?使用?@Scheduled?注解和?TaskScheduler?接口實(shí)現(xiàn)定時(shí)任務(wù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06關(guān)于Java兩個(gè)浮點(diǎn)型數(shù)字加減乘除的問題
由于浮點(diǎn)數(shù)在計(jì)算機(jī)中是以二進(jìn)制表示的,直接進(jìn)行加減乘除運(yùn)算會出現(xiàn)精度誤差,想要得到精確結(jié)果,應(yīng)使用BigDecimal類進(jìn)行運(yùn)算2024-10-10