JVM垃圾收集和优化
總覽
在對系統(tǒng)進(jìn)行性能相關(guān)問題的故障排除時(shí),內(nèi)存優(yōu)化是一個(gè)需要深入分析每個(gè)系統(tǒng)在內(nèi)存中存儲的內(nèi)容,存儲時(shí)間和訪問模式的地方。 這篇文章是要在背景信息上進(jìn)行注釋,并在此工作中要注意一些要點(diǎn),這些工作要專門針對基于Java的實(shí)現(xiàn),因?yàn)樯钊肓私釰VM行為在此過程中非常有益。
Java語言通過在很大程度上照顧內(nèi)存管理,從而將重點(diǎn)放在其余邏輯上,為開發(fā)人員提供了許多便利。 仍然對Java到底是如何做的有一個(gè)很好的了解,合理化我們在Java實(shí)現(xiàn)中遵循的幾種最佳實(shí)踐,并幫助更好地設(shè)計(jì)程序,并認(rèn)真思考某些方面,從長遠(yuǎn)來看,這些方面以后可能導(dǎo)致內(nèi)存泄漏和系統(tǒng)穩(wěn)定性。 Java Garbage Collector在這方面起著重要作用,它負(fù)責(zé)通過刪除內(nèi)存垃圾來釋放內(nèi)存。
這些信息可廣泛獲得,但我在這里匯總以供參考。 :)
JVM使Java代碼能夠以獨(dú)立于硬件和OS的方式運(yùn)行。 它在由操作系統(tǒng)充當(dāng)物理機(jī)的另一種抽象的操作系統(tǒng)分配給自己的進(jìn)程的內(nèi)存位置上運(yùn)行。
JVM可以基于[1]中發(fā)布??的開放標(biāo)準(zhǔn)來實(shí)現(xiàn),眾所周知的實(shí)現(xiàn)是Oracle Hotspot JVM,幾乎與Android OS中使用的開放源代碼版本OpenJDK,IBM J9,JRockit和Dalvik VM有所不同。
簡而言之,JVM使用從平臺分配給它的資源來加載并執(zhí)行已編譯的Java字節(jié)代碼,它在上面運(yùn)行。
類加載器
將字節(jié)碼加載到JVM內(nèi)存中(加載,鏈接(驗(yàn)證,準(zhǔn)備,解決–>如果發(fā)出失敗的NoClassDef發(fā)現(xiàn)異常),初始化),引導(dǎo)類加載器,擴(kuò)展類加載器,應(yīng)用程序類加載器
內(nèi)存和運(yùn)行時(shí)數(shù)據(jù)區(qū)
盡管它并不全面,但它涵蓋了以下幾個(gè)重要部分。
- 本機(jī)方法堆棧– Java本機(jī)庫堆棧,它與平臺有關(guān),主要是用C語言編寫的。
- JVM堆棧(每個(gè)線程保留當(dāng)前執(zhí)行的方法堆棧跟蹤。如果未設(shè)置適當(dāng)?shù)闹袛?#xff0c;則遞歸方法調(diào)用可能導(dǎo)致堆棧被填充和溢出(java.lang.StackOverFlowError) 。-Xss JVM選項(xiàng)允許配置堆棧大小。),PC寄存器(程序計(jì)數(shù)器,指向每個(gè)線程要執(zhí)行的下一條指令。)
- 方法區(qū)域(存儲類數(shù)據(jù),大小受XX:MaxPermSize限制 ,PermGen空間默認(rèn)為64MB,如果要服務(wù)于加載數(shù)百萬個(gè)類的大型服務(wù)器應(yīng)用程序,則可以通過增加調(diào)整來避免OOM問題:PermGen空間。從Java 8開始)自此以后,盡管允許對其進(jìn)行微調(diào)和限制,但默認(rèn)情況下java8中將此PermGen空間稱為沒有限制的元空間),Heap(Xms,Xmx),運(yùn)行時(shí)常量池
執(zhí)行引擎
該引擎執(zhí)行通過類加載器分配給運(yùn)行時(shí)數(shù)據(jù)區(qū)域的字節(jié)碼。 它利用解釋器,垃圾收集器,熱點(diǎn)分析器,JIT編譯器來優(yōu)化程序執(zhí)行。
有關(guān)JVM體系結(jié)構(gòu)的更多詳細(xì)信息,請參見[2]。
現(xiàn)在我們知道了垃圾收集器在JVM體系結(jié)構(gòu)中的位置。 讓我們深入了解內(nèi)部。
垃圾收集器
這是Java自動(dòng)內(nèi)存管理過程,它刪除了不再使用的對象。 接下來是問題,它如何決定是否使用該對象。
它定義了兩類對象,
活動(dòng)對象 –從另一個(gè)對象引用的可訪問對象。 最終,引用鏈接將到達(dá)根,該根是創(chuàng)建整個(gè)對象圖的主線程。 死對象 –只是躺在堆中的其他對象未引用的不可訪問對象。
此分類和垃圾回收基于以下兩個(gè)事實(shí)。
1.大多數(shù)對象在創(chuàng)建后很快就變得無法訪問。 通常,短暫對象僅存在于方法上下文中。 2.老物件很少指年輕物件。 例如,壽命長的緩存幾乎不會從中引用較新的對象。
新創(chuàng)建的對象實(shí)例駐留在Java堆中,該堆可以進(jìn)行不同的生成,如下所示。 垃圾收集是通過稱為“垃圾收集器”的守護(hù)程序線程完成的,該線程將對象引導(dǎo)通過堆中的不同空間。
垃圾收集分3個(gè)步驟進(jìn)行。
1.標(biāo)記 –從根開始并遍歷對象圖,將可訪問對象標(biāo)記為活動(dòng)對象。
2.掃描 –刪除未標(biāo)記的對象。 3.緊湊 –對內(nèi)存進(jìn)行碎片整理,使活動(dòng)對象的分配連續(xù)。 這被認(rèn)為是最耗時(shí)的過程。
堆區(qū)域劃分如下。
舊的(終身使用的)代 –可以存活很長時(shí)間的對象,請留在這里,直到其被標(biāo)記為無法訪問并在遍及整個(gè)堆的主要垃圾收集中清理為止。
年輕一代 –進(jìn)一步分為3個(gè)伊甸園空間和2個(gè)幸存者空間。 垃圾收集分為兩個(gè)階段:“次要”或“主要”。 這兩個(gè)垃圾回收都是停止運(yùn)行的操作,它們停止所有其他內(nèi)存訪問。 應(yīng)用程序可能不會感覺到次要GC,因?yàn)樗鼉H掃描年輕一代空間會很小。
垃圾收集器
內(nèi)存生命周期如下圖所示,如上圖所示。
1.新創(chuàng)建的對象駐留在Eden空間中。 (就像人類從伊甸園開始的一樣:)在伊甸園空間變滿之前,它一直在不斷增加新的物體。
2.當(dāng)Eden空間已滿時(shí),將運(yùn)行次要GC,標(biāo)記活動(dòng)對象,然后將這些活動(dòng)對象移至“幸存者從”空間,然后掃掠空閑的Eden空間。
3.然后在程序運(yùn)行時(shí)繼續(xù)用新對象填充Eden空間。 現(xiàn)在,當(dāng)Eden空間已滿時(shí),我們先前也已將“幸存者來自”空間中的對象移動(dòng)了。 次要GC在這兩個(gè)空間中運(yùn)行標(biāo)記對象,然后將剩余的活動(dòng)對象整體移至其他幸存者空間。 想知道為什么不將有生命的物體從伊甸園空間復(fù)制到“幸存者從”的剩余空間,而不是全部轉(zhuǎn)移到另一個(gè)幸存者空間? 好吧,事實(shí)證明,在緊湊的步驟中,將所有其他對象移到另一個(gè)對象上要比壓縮其中帶有對象的區(qū)域更為有效。
4.此循環(huán)將在幸存者空間之間重復(fù)移動(dòng)對象,直到達(dá)到配置的閾值(-XX: MaxTenuringThreshold ) 。 (它跟蹤每個(gè)對象生存了多少個(gè)GC循環(huán))。 當(dāng)達(dá)到閾值時(shí),這些對象將被移至保管空間。
5.隨著時(shí)間的流逝,如果使用權(quán)空間也被填滿,則主GC將啟動(dòng)并遍歷整個(gè)堆內(nèi)存空間,以執(zhí)行GC步驟。 這種暫停可以在人際互動(dòng)中感覺到,這是不希望的。
當(dāng)內(nèi)存泄漏或長時(shí)間駐留大量高速緩存時(shí),使用期限將被占用。 在這種時(shí)候,這些對象甚至可能沒有被檢測為死亡。 這會導(dǎo)致主要GC頻繁運(yùn)行,因?yàn)樗鼨z測到永久性空間已滿,但是由于無法清除任何內(nèi)容,因此無法清理足夠的內(nèi)存。
當(dāng)內(nèi)存不足時(shí),日志中的錯(cuò)誤“ java.lang.OutOfMemoryError”將清楚地提示我們。 另外,如果我們看到頻繁使用大量內(nèi)存的CPU頻繁運(yùn)行,則可能是由于某些內(nèi)存處理問題需要引起注意而導(dǎo)致GC頻繁運(yùn)行的征兆。
在專注于JVM微調(diào)(專注于內(nèi)存利用率)時(shí),主要的決定因素是響應(yīng)性/延遲和吞吐量中更關(guān)鍵的因素。 如果在批處理中吞吐量是最重要的,那么如果可以提高主要吞吐量,我們可以在運(yùn)行主要GC時(shí)暫停一下來妥協(xié)。 因?yàn)閼?yīng)用程序偶爾的響應(yīng)速度可能不是那里的問題。
另一方面,如果像在基于UI的應(yīng)用程序中一樣,響應(yīng)性至關(guān)重要,則應(yīng)嘗試避免使用大型GC。 也就是說,這樣做并沒有幫助。 例如,我們可以通過增加年輕一代的空間來延遲大型GC。 但是隨后,小型GC將開始花費(fèi)大量時(shí)間,因?yàn)樗F(xiàn)在需要遍歷并壓縮巨大的空間。 因此,要擁有正確的尺寸,就需要謹(jǐn)慎地實(shí)現(xiàn)年輕人和老年人之間的正確比例。 有時(shí),這甚至可以進(jìn)入應(yīng)用程序設(shè)計(jì)細(xì)節(jié),以通過對象創(chuàng)建模式和緩存位置來微調(diào)內(nèi)存使用情況。 這將是另一篇文章的主題,該文章分析堆轉(zhuǎn)儲和火焰圖,以確定要緩存的最佳內(nèi)容。
垃圾收集器
由于垃圾回收的作用對應(yīng)用程序的性能有很大的影響,因此工程師們花費(fèi)了大量的精力來改進(jìn)它。 結(jié)果是,我們可以根據(jù)需求選擇最佳的垃圾收集器。 以下是不完整的選項(xiàng)列表。
1.串行收集器
在單個(gè)線程中運(yùn)行。 僅適用于基本應(yīng)用。
一個(gè)線程執(zhí)行垃圾回收。 它只會使世界處于標(biāo)記和重新標(biāo)記階段。 其余的工作在應(yīng)用程序運(yùn)行時(shí)完成,并且不等待舊的版本已滿。 當(dāng)內(nèi)存空間大,具有大量CPU來滿足并發(fā)執(zhí)行時(shí),以及當(dāng)應(yīng)用程序要求最短的暫停時(shí)間和響應(yīng)能力成為關(guān)鍵因素時(shí),這是一個(gè)不錯(cuò)的選擇。 過去,這是大多數(shù)Web應(yīng)用程序中最受歡迎的。
3.并行收集器
該收集器使用多個(gè)CPU。 它等待舊的一代充滿或接近充滿,但是當(dāng)它運(yùn)行時(shí),它停止了世界。 多個(gè)線程進(jìn)行標(biāo)記,清除和壓縮,使垃圾收集更快。 當(dāng)內(nèi)存不是很大并且CPU數(shù)量受到限制時(shí),這是一個(gè)很好的選擇,可以滿足對吞吐量的要求,可以承受暫停。
4. G1(垃圾優(yōu)先)收集器(1.7以上)
通過允許配置(例如,GC運(yùn)行時(shí)暫停時(shí)間),此選項(xiàng)可提高垃圾收集的可預(yù)測性。 據(jù)說在并行性和并發(fā)性兩方面都具有優(yōu)勢。 它將內(nèi)存劃分為多個(gè)區(qū)域,每個(gè)區(qū)域都被視為伊甸園,幸存者或保有權(quán)空間。 如果該區(qū)域有更多無法訪問的對象,則將首先對該區(qū)域進(jìn)行垃圾收集。
版本中的默認(rèn)垃圾收集器
- Java 7 –并行GC
- Java 8 –并行GC
- Java 9 – G1 GC
- Java 10 – G1 GC
- Java 11 – G1 GC(ZGC與Epsilon一起作為實(shí)驗(yàn)功能提供)
- Java 12 – G1 GC(引入了Shenandoah GC。僅適用于OpenJDK。)
垃圾收集器的優(yōu)化參數(shù)
除非有默認(rèn)設(shè)置要解決的問題,或者是經(jīng)過長時(shí)間的考慮,并且經(jīng)過長時(shí)間的生產(chǎn)級別的負(fù)載模式驗(yàn)證后才能確定,否則調(diào)優(yōu)JVM的經(jīng)驗(yàn)法則是不這樣做的。 這是因?yàn)镴ava Ergonomics已經(jīng)取得了很大進(jìn)步,并且如果應(yīng)用程序不難看的話,大多數(shù)時(shí)候?qū)⒛軌驁?zhí)行很多優(yōu)化。 在[5]中可以找到選項(xiàng)的完整列表,包括配置堆空間的大小,閾值,要使用的垃圾收集器的類型等。
診斷
除堆轉(zhuǎn)儲外,以下配置還有助于通過GC行為診斷內(nèi)存問題。
-XX:-PrintGCDetails –打印垃圾收集的詳細(xì)信息。
-Xloggc:<文件名> –將GC日志記錄詳細(xì)信息打印到給定文件。
-XX:-UseGCLogFileRotation –完成上述配置后,啟用GC日志文件旋轉(zhuǎn)。 -XX:-HeapDumpOnOutOfMemoryError –如果發(fā)生OOM錯(cuò)誤,則轉(zhuǎn)儲堆內(nèi)容以進(jìn)行進(jìn)一步分析。 -XX:OnOutOfMemoryError =” <cmd args =””>; <cmd args =””>” –如果發(fā)生OOM錯(cuò)誤,則要運(yùn)行的命令集。 遇到錯(cuò)誤時(shí)允許執(zhí)行任何自定義任務(wù)。
我們將在另一篇文章中討論診斷和分析細(xì)節(jié)。
干杯!
[1] – https://docs.oracle.com/javase/specs/index.html
[2] – https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.6
[2] – Oracle垃圾收集調(diào)優(yōu)指南–
https://docs.oracle.com/javase/9??/gctuning/ergonomics.htm#JSGCT-GUID-DB4CAE94-2041-4A16-90EC-6AE3D91EC1F1
[3] –新的Java垃圾收集器–
https://blogs.oracle.com/javamagazine/understanding-the-jdks-new-superfast-garbage-collectors
[4] –可用的收藏家–
https://docs.oracle.com/cn/java/javase/13/gctuning/available-collectors.html#GUID-F215A508-9E58-40B4-90A5-74E29BF3BD3C
[5] – JVM選項(xiàng)–
https://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html
翻譯自: https://www.javacodegeeks.com/2020/05/jvm-garbage-collection-and-optimizations.html
總結(jié)
以上是生活随笔為你收集整理的JVM垃圾收集和优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jolokia_Hawtio和Jolok
- 下一篇: 公司linux C++开发用哪个linu