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