Java記憶體與垃圾回收調優

来源:https://www.cnblogs.com/lfs2640666960/archive/2018/08/10/9457856.html
-Advertisement-
Play Games

要瞭解Java垃圾收集機制,先理解JVM記憶體模式是非常重要的。今天我們將會瞭解JVM記憶體的各個部分、如何監控以及垃圾收集調優。 ...


要瞭解Java垃圾收集機制,先理解JVM記憶體模式是非常重要的。今天我們將會瞭解JVM記憶體的各個部分、如何監控以及垃圾收集調優。

Java(JVM)記憶體模型

正如你從上面的圖片看到的,JVM記憶體被分成多個獨立的部分。廣泛地說,JVM堆記憶體被分為兩部分——年輕代**(Young Generation)和老年代(Old Generation)**。

年輕代

年輕代是所有新對象產生的地方。當年輕代記憶體空間被用完時,就會觸發垃圾回收。這個垃圾回收叫做Minor GC。年輕代被分為3個部分——Enden區和兩個Survivor區。

 

年輕代空間的要點:

  • 大多數新建的對象都位於Eden區。

  • 當Eden區被對象填滿時,就會執行Minor GC。並把所有存活下來的對象轉移到其中一個survivor區。

  • Minor GC同樣會檢查存活下來的對象,並把它們轉移到另一個survivor區。這樣在一段時間內,總會有一個空的survivor區。

  • 經過多次GC周期後,仍然存活下來的對象會被轉移到年老代記憶體空間。通常這是在年輕代有資格提升到年老代前通過設定年齡閾值來完成的。

年老代

年老代記憶體里包含了長期存活的對象和經過多次Minor GC後依然存活下來的對象。通常會在老年代記憶體被占滿時進行垃圾回收。老年代的垃圾收集叫做Major GC。Major GC會花費更多的時間。

Stop the World事件

所有的垃圾收集都是“Stop the World”事件,因為所有的應用線程都會停下來直到操作完成(所以叫“Stop the World”)。

因為年輕代里的對象都是一些臨時(short-lived )對象,執行Minor GC非常快,所以應用不會受到(“Stop the World”)影響。

由於Major GC會檢查所有存活的對象,因此會花費更長的時間。應該儘量減少Major GC。因為Major GC會在垃圾回收期間讓你的應用反應遲鈍,所以如果你有一個需要快速響應的應用發生多次Major GC,你會看到超時錯誤。

垃圾回收時間取決於垃圾回收策略。這就是為什麼有必要去監控垃圾收集和對垃圾收集進行調優。從而避免要求快速響應的應用出現超時錯誤。

永久代

永久代或者“Perm Gen”包含了JVM需要的應用元數據,這些元數據描述了在應用里使用的類和方法。註意,永久代不是Java堆記憶體的一部分。

永久代存放JVM運行時使用的類。永久代同樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。

方法區

方法區是永久代空間的一部分,並用來存儲類型信息(運行時常量和靜態變數)和方法代碼和構造函數代碼。

記憶體池

如果JVM實現支持,JVM記憶體管理會為創建記憶體池,用來為不變對象創建對象池。字元串池就是記憶體池類型的一個很好的例子。記憶體池可以屬於堆或者永久代,這取決於JVM記憶體管理的實現。

運行時常量池

運行時常量池是每個類常量池的運行時代表。它包含了類的運行時常量和靜態方法。運行時常量池是方法區的一部分。

Java棧記憶體

Java棧記憶體用於運行線程。它們包含了方法里的臨時數據、堆里其它對象引用的特定數據。你可以閱讀棧記憶體和堆記憶體的區別。

Java 堆記憶體開關

Java提供了大量的記憶體開關(參數),我們可以用它來設置記憶體大小和它們的比例。下麵是一些常用的開關:

大多數時候,上面的選項已經足夠使用了。但是如果你還想瞭解其他的選項,那麼請查看JVM選項官方網頁。

Java垃圾回收

Java垃圾回收會找出沒用的對象,把它從記憶體中移除並釋放出記憶體給以後創建的對象使用。Java程式語言中的一個最大優點是自動垃圾回收,不像其他的程式語言那樣需要手動分配和釋放記憶體,比如C語言。

垃圾收集器是一個後臺運行程式。它管理著記憶體中的所有對象並找出沒被引用的對象。所有的這些未引用的對象都會被刪除,回收它們的空間並分配給其他對象。

一個基本的垃圾回收過程涉及三個步驟:

  1. 標記:這是第一步。在這一步,垃圾收集器會找出哪些對象正在使用和哪些對象不在使用。

  2. 正常清除:垃圾收集器清會除不在使用的對象,回收它們的空間分配給其他對象。

  3. 壓縮清除:為了提升性能,壓縮清除會在刪除沒用的對象後,把所有存活的對象移到一起。這樣可以提高分配新對象的效率。

簡單標記和清除方法存在兩個問題:

  1. 效率很低。因為大多數新建對象都會成為“沒用對象”。

  2. 經過多次垃圾回收周期的對象很有可能在以後的周期也會存活下來。

上面簡單清除方法的問題在於Java垃圾收集的分代回收的,而且在堆記憶體里有年輕代和年老代兩個區域。我已經在上面解釋了Minor GC和Major GC是怎樣掃描對象,以及如何把對象從一個分代空間移到另外一個分代空間。

Java垃圾回收類型

這裡有五種可以在應用里使用的垃圾回收類型。僅需要使用JVM開關就可以在我們的應用里啟用垃圾回收策略。讓我們一起來逐一瞭解:

  1. Serial GC(-XX:+UseSerialGC):Serial GC使用簡單的標記、清除、壓縮方法對年輕代和年老代進行垃圾回收,即Minor GC和Major GC。Serial GC在client模式(客戶端模式)很有用,比如在簡單的獨立應用和CPU配置較低的機器。這個模式對占有記憶體較少的應用很管用。

  2. Parallel GC(-XX:+UseParallelGC):除了會產生N個線程來進行年輕代的垃圾收集外,Parallel GC和Serial GC幾乎一樣。這裡的N是系統CPU的核數。我們可以使用 -XX:ParallelGCThreads=n 這個JVM選項來控制線程數量。並行垃圾收集器也叫throughput收集器。因為它使用了多CPU加快垃圾回收性能。Parallel GC在進行年老代垃圾收集時使用單線程。

  3. Parallel Old GC(-XX:+UseParallelOldGC):和Parallel GC一樣。不同之處,Parallel Old GC在年輕代垃圾收集和年老代垃圾回收時都使用多線程收集。

  4. 併發標記清除(CMS)收集器(-XX:+UseConcMarkSweepGC):CMS收集器也被稱為短暫停頓併發收集器。它是對年老代進行垃圾收集的。CMS收集器通過多線程併發進行垃圾回收,儘量減少垃圾收集造成的停頓。CMS收集器對年輕代進行垃圾回收使用的演算法和Parallel收集器一樣。這個垃圾收集器適用於不能忍受長時間停頓要求快速響應的應用。可使用 -XX:ParallelCMSThreads=n JVM選項來限制CMS收集器的線程數量。

  5. G1垃圾收集器(-XX:+UseG1GC) G1(Garbage First):垃圾收集器是在Java 7後才可以使用的特性,它的長遠目標時代替CMS收集器。G1收集器是一個並行的、併發的和增量式壓縮短暫停頓的垃圾收集器。G1收集器和其他的收集器運行方式不一樣,不區分年輕代和年老代空間。它把堆空間劃分為多個大小相等的區域。當進行垃圾收集時,它會優先收集存活對象較少的區域,因此叫“Garbage First”。你可以在Oracle Garbage-FIrst收集器文檔找到更多詳細信息。

Java垃圾收集監控

我們可以使用命令行和圖形工具來監控監控應用垃圾回收。例如,我使用Java SE下載頁中的一個demo來實驗。

如果你想使用同樣的應用,可以到Java SE下載頁面下載JDK 7和JavaFX演示和示例。我使用的示例應用是Java2Demo.jar,它位於 jdk1.7.0_55/demo/jfc/Java2D 目錄下。這隻是一個可選步驟,你可以運行GC監控命令監控任何Java應用。

我打開演示應用使用的命令是:

1. pankaj@Pankaj:~/Downloads/jdk1.7.0_55/demo/jfc/Java2D$ java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar

jsat

可以使用jstat命令行工具監控JVM記憶體和垃圾回收。標準的JDK已經附帶了jstat,所以不需要做任何額外的事情就可以得到它。

要運行jstat你需要知道應用的進程id,你可以使用 ps -eaf | grep java 命令獲取進程id。

1.pankaj@Pankaj:~$ ps -eaf | grep Java2Demo.jar 
2.501 9582 11579 0 9:48PM ttys000 0:21.66 /usr/bin/java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar
3.501 14073 14045 0 9:48PM ttys002 0:00.00 grep Java2Demo.jar

從上面知道,我的Java應用進程id是9582。現在可以運行jstat命令了,就像下麵展示的一樣:

1. pankaj@Pankaj:~$ jstat -gc 9582 1000 2. S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 3. 1024.0 1024.0 0.0 0.0 8192.0 7933.3 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654 4. 1024.0 1024.0 0.0 0.0 8192.0 8026.5 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654 5. 1024.0 1024.0 0.0 0.0 8192.0 8030.0 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654 6. 1024.0 1024.0 0.0 0.0 8192.0 8122.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654 7. 1024.0 1024.0 0.0 0.0 8192.0 8171.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654 8. 1024.0 1024.0 48.7 0.0 8192.0 106.7 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656 9. 1024.0 1024.0 48.7 0.0 8192.0 145.8 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656

jstat命令的最後一個參數是每個輸出的時間間隔。每隔一秒就會列印出記憶體和垃圾收集數據。
讓我們一起來對每一列的意義進行逐一瞭解:

  • S0C和S1C:這一列展示了Survivor0和Survivor1區的當前大小(單位KB)。

  • S0U和S1U:這一列展示了當前Survivor0和Survivor1區的使用情況(單位KB)。註意:無論任何時候,總會有一個Survivor區是空著的。

  • EC和EU:這些列展示了Eden區當前空間大小和使用情況(單位KB)。註意:EU的大小一直在增大。而且只要大小接近EC時,就會觸發Minor GC並且EU將會減小。

  • OC和OU:這些列展示了年老代當前空間大小和當前使用情況(單位KB)。

  • PC和PU:這些列展示了Perm Gen(永久代)當前空間大小和當前使用情況(單位KB)。

  • YGC和YGCT:YGC這列顯示了發生在年輕代的GC事件的數量。YGCT這列顯示了在年輕代進行GC操作的累計時間。註意:在EU的值由於minor GC導致下降時,同一行的YGC和YGCT都會增加。

  • FGC和FGCT:FGC列顯示了發生Full GC事件的次數。FGCT顯示了進行Full GC操作的累計時間。註意:相對於年輕代的GC使用時間,Full GC所用的時間長很多。

  • GCT:這一列顯示了GC操作的總累計時間。註意:總累計時間是YGCT和FGCT兩列所用時間的總和(GCT=YGCT+FGCT)。

jstat的優點,我們同樣可以在沒有GUI的遠程伺服器上運行jstat。註意:我們是通過 -Xmn10m 選項來指定S0C、S1C和EC的總和為10m的。

Java VisualVM及Visual GC插件

如果你想在GUI里查看記憶體和GC,那麼可以使用jvisualvm工具。Java VisualVM同樣是JDK的一部分,所以你不需要單獨去下載。

在終端運行jvisualvm命令啟動Java VisualVM程式。一旦啟動程式,你需要從Tools->Plugins選項安裝Visual GC插件,就像下麵圖片展示的。

安裝完Visual GC插件後,從左邊欄打開應用並把視角轉到Visual GC部分。你將會得到關於JVM記憶體和垃圾收集詳情,如下圖所示。

Java垃圾回收調優

Java垃圾回收調優應該是提升應用吞吐量的最後一個選擇。在你發現應用由於長時間垃圾回收導致了應用性能下降、出現超時的時候,應該考慮Java垃圾收集調優。

如果你在日誌里看到

java.lang.OutOfMemoryError: PermGen space錯誤,那麼可以嘗試使用 -XX:PermGen 和 -XX:MaxPermGen JVM選項去監控並增加Perm Gen記憶體空間。你也可以嘗試使用

-XX:+CMSClassUnloadingEnabled並查看使用CMS垃圾收集器的執行性能。

如果你看到了大量的Full GC操作,那麼你應該嘗試增大老年代的記憶體空間。

全面垃圾收集調優要花費大量的努力和時間,這裡沒有一塵不變的硬性調優規則。你需要去嘗試不同的選項並且對這些選項進行對比,從而找出最適合自己應用的方案。

這就是所有的Java記憶體模型和垃圾回收內容。希望對你理解JVM記憶體和垃圾收集過程有所幫助。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 給定兩個二進位字元串,返回他們的和(用二進位表示)。 輸入為非空字元串且只包含數字 1 和 0。 示例 1: 輸入: a = "11", b = "1" 輸出: "100" 示例 2: 輸入: a = "1010", b = "1011" 輸出: "10101" class Solution: de ...
  • 前言 本人並不會PHP,但因工作需要瞭解PHP代碼故學習搭建環境,記錄一下 系統:macOS High Sierra(10.13.6)自帶PHP為7.1(使用php -v查看版本 公司PHP版本5.6,其他版本可能也適用 1、安裝php5.6 brew install [email protected] (如果沒有安裝 ...
  • 給定一個非負整數組成的非空數組,在該數的基礎上加一,返回一個新的數組。 最高位數字存放在數組的首位, 數組中每個元素只存儲一個數字。 你可以假設除了整數 0 之外,這個整數不會以零開頭。 示例 1: 輸入: [1,2,3] 輸出: [1,2,4] 解釋: 輸入數組表示數字 123。 示例 2: 輸入 ...
  • 準備工作: 安裝itchat:安裝命令pip install itchat 1.導入itchat模塊,給文件傳輸助手發一句話 運行代碼,掃二維碼,也就是登陸網頁版微信,會自動將"你好,文件傳輸助手"傳輸到手機上。 itchat.send()是發送消息,“”裡面是想要發送的內容,toUserName是 ...
  • 雖然我是在jdk10環境下, 但是大體上和jdk8是差不多的. 總共有這麼多 本來想著一口氣把所有命令都邊學邊總結一下的, 結果發現....有些還真的不是很常用....或者說我這個水平還接觸不到那麼多. 於是我就把我能使用到的幾個常用的總結一下了. 當做是筆記了. jshell jshell啟動 j ...
  • 從最初的“Hello World”,走到面向對象。該回過頭來看看,教程中是否遺漏了什麼。 我們之前提到一句話,"Everything is Object". 那麼我們就深入體驗一下這句話。   需要先要介紹兩個內置函數,dir()和help() dir()用來查詢一個類或者對象所有屬性。你 ...
  •   函數最重要的目的是方便我們重覆使用相同的一段程式。 將一些操作隸屬於一個函數,以後你想實現相同的操作的時候,只用調用函數名就可以,而不需要重覆敲所有的語句。   函數的定義 首先,我們要定義一個函數, 以說明這個函數的功能。 def square_sum(a,b): c = ...
  • java中的多線程 Java是少數的幾種支持“多線程”的編程語言之一。大多數的編程語言只能順序運行多個單獨程式塊,無法同時運行不同的多個程式塊。java的“多線程”恰可以彌補這個缺憾,它可讓不同的程式塊併發執行,如此一來就可讓程式塊運行得更為流暢,同時也可達到多任務處理的目的。 先看一下單線程的情況 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...