03 JVM的垃圾回收機制

来源:https://www.cnblogs.com/nnngu/archive/2018/03/01/8490393.html
-Advertisement-
Play Games

1、前言 理解JVM的垃圾回收機制(簡稱GC)有什麼好處呢?作為一名軟體開發者,滿足自己的好奇心將是一個很好的理由,不過更重要的是,理解GC工作機制可以幫助你寫出更好的Java程式。 在學習GC前,你應該知道一個技術名詞:“stop the world” ,無論你選擇哪種GC演算法,“stop the ...


1、前言

理解JVM的垃圾回收機制(簡稱GC)有什麼好處呢?作為一名軟體開發者,滿足自己的好奇心將是一個很好的理由,不過更重要的是,理解GC工作機制可以幫助你寫出更好的Java程式。

在學習GC前,你應該知道一個技術名詞:“stop-the-world” ,無論你選擇哪種GC演算法,“stop-the-world”都會發生。“stop-the-world”意味著JVM停止應用程式,而去進行垃圾回收。當“stop-the-world”發生時,除了進行垃圾回收的線程,其他所有線程都將停止運行。被中斷的任務將在GC任務完成後恢復執行。GC調優往往意味著減少“stop-the-world”的時間。

2、分代垃圾收集機制

在HotSpot虛擬機中,將記憶體分為 年輕代(young generation)、老年代(old generation) 和 永久代(permanent generation)

我們看一下這幅圖:

年輕代: 新創建的對象都存放在這裡。因為大多數對象很快變得不可達,所以大多數對象在年輕代中創建,然後消失。當對象從這塊記憶體區域消失時,我們說發生了一次“minor GC”。

老年代: 沒有變得不可達,存活下來的年輕代對象被覆制到這裡。這塊記憶體區域一般大於年輕代。因為它更大的規模,GC發生的次數比在年輕代的少。對象從老年代消失時,我們說“major GC”(或“full GC”)發生了。

永久代: 永久代(permanent generation) 也稱為“方法區(method area)”,它存儲class對象和字元串常量。所以這塊記憶體區域絕對不是永久的存放從老年代存活下來的對象的。發生在這裡的垃圾回收也被稱為major GC。

3、垃圾收集演算法

垃圾收集演算法如下:

  • 年輕代-複製演算法
  • 老年代-標記清除演算法
  • 老年代-標記整理演算法
  • 永久代-方法區回收

年輕代-複製演算法

該演算法的核心是將可用記憶體按容量劃分為大小相等的兩塊,每次只用其中一塊,當這一塊的記憶體用完,就將還存活的對象複製到另外一塊上面,然後把已使用過的記憶體空間一次清理掉。這使得每次只對其中一塊記憶體進行回收,分配也就不用考慮記憶體碎片等複雜情況,實現簡單且運行高效。如下圖:

老年代-標記清除演算法

該演算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象(可達性分析),在標記完成後統一清理掉所有被標記的對象。如下圖:

該演算法會有以下兩個問題:

  1. 效率問題:標記過程和清除過程的效率都不高;
  2. 空間問題:標記清除後會產生大量不連續的記憶體碎片,空間碎片太多可能會導致在運行過程中需要分配較大對象時無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集。

老年代-標記整理演算法

標記清除演算法會產生記憶體碎片,而複製演算法需要有額外的記憶體擔保空間,於是針對老年代的特點,又有了標記整理演算法。標記整理演算法的標記過程與標記清除演算法相同,但後續步驟不再對可回收對象直接清理,而是讓所有存活的對象都向一端移動,然後清理掉這一端邊界以外的記憶體。如下圖:

永久代-方法區回收

在方法區進行垃圾回收一般“性價比”較低,因為在方法區主要回收兩部分內容:廢棄常量無用的類

回收廢棄常量與回收其他年代中的對象類似。

但是判斷一個類是不是無用的類的條件則相當苛刻:

  • 該類所有的實例都已經被回收,Java堆中不存在該類的任何實例;
  • 該類對應的Class對象沒有在任何地方被引用;
  • 載入該類的ClassLoader已經被回收;

但即使滿足以上條件也未必一定會回收,Hotspot VM還提供了-Xnoclassgc參數控制(關閉CLASS的垃圾回收功能)。因此在大量使用動態代理、CGLib等位元組碼框架的應用中一定要關閉該選項,開啟VM的類卸載功能,以保證方法區不會溢出。

4、垃圾回收的兩個重要方法

System.gc()方法 和 finalize()方法

System.gc()方法

使用System.gc() 可以不管JVM使用的是哪一種垃圾回收的演算法,都可以請求Java的垃圾回收。在命令行中有一個參數-verbosegc可以查看Java使用的堆記憶體的情況,它的格式如下:java -verbosegc classfile 由於這種方法會影響系統性能,不推薦使用,所以不詳細介紹。

finalize()方法

JVM垃圾回收器在回收一個對象之前,一般要求程式調用適當的方法釋放資源,但在沒有明確釋放資源的情況下,Java提供了預設機制來終止該對象從而釋放資源,這個方法就是finalize() 。它的原型為:protected void finalize() throws Throwable 在finalize() 方法返回之後,對象消失,垃圾收集開始執行。原型中的throws Throwable 表示它可以拋出任何類型的異常。

之所以要使用finalize(),是因為存在著垃圾回收器不能處理的特殊情況。例如:

  1. 由於在分配記憶體的時候可能採用了類似 C語言的做法,而非Java通常的new 。這種情況主要發生在 native 方法中,比如 native 方法調用了C/C++malloc()函數來分配存儲空間,除非調用 free() 函數,否則這些記憶體空間將不會得到釋放,那麼這個時候就可能造成記憶體泄漏。但是由於 free() 是C/C++中的函數,所以 finalize() 中可以用本地方法來調用它。以釋放這些“特殊”的記憶體空間。

  2. 或者是打開的文件資源,這些資源不屬於垃圾回收器的回收範圍。

5、觸發GC(Garbage Collector)的條件

  1. GC在優先順序最低的線程中運行,一般在應用程式空閑即沒有應用線程在運行時被調用。但下麵的條件例外。

  2. Java堆記憶體不足時,GC會被調用。當應用線程在運行,併在運行過程中創建新對象,若這時記憶體空間不足,JVM就會強制調用GC線程。若GC一次之後仍不能滿足記憶體分配,JVM會再進行兩次GC,若仍無法滿足要求,則JVM將報“out of memory”的錯誤,Java應用將停止。

6、減少GC開銷的措施

  1. 不要顯式調用System.gc() 。此函數建議JVM進行主GC,雖然只是建議而非一定,但很多情況下它會觸發主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數。大大的影響系統性能。

  2. 儘量減少臨時對象的使用。臨時對象在跳出函數調用後,會成為垃圾,少用臨時變數就相當於減少了垃圾的產生,從而延長了出現上述第二個觸發條件出現的時間,減少了主GC的機會。

  3. 對象不用時最好顯式置為Null。一般而言,為Null的對象都會被作為垃圾處理,所以將不用的對象顯式地設為Null,有利於GC收集器判定垃圾,從而提高了GC的效率。

  4. 儘量使用StringBuffer,而不用String來累加字元串。由於String是固定長的字元串對象,累加String對象時,並非在一個String對象中擴增,而是重新創建新的String對象,如 Str5=Str1+Str2+Str3+Str4; 這條語句執行過程中會產生多個垃圾對象,因為每次作“+”操作時都必須創建新的String對象,但這些過渡對象對系統來說是沒有實際意義的,只會增加更多的垃圾。避免這種情況可以改用StringBuffer來累加字元串,因StringBuffer是可變長的,它在原有基礎上進行擴增,不會產生中間對象。

  5. 能用基本類型如 int, long 就不用包裝類型Integer, Long。基本類型變數占用的記憶體資源比包裝類型占用的少得多,如果沒有必要,最好使用基本變數。

  6. 儘量少用靜態對象變數。靜態變數屬於全局變數,不會被GC回收,它們會一直占用記憶體。

  7. 分散對象創建或刪除的時間。集中在短時間內大量創建新對象,特別是大對象,會導致突然需要大量記憶體,JVM在面臨這種情況時,只能進行主GC,以回收記憶體或整合記憶體碎片,從而增加主GC的頻率。集中刪除對象,道理也是一樣的。它使得突然出現了大量的垃圾對象,空閑空間必然減少,從而大大增加了下一次創建新對象時強制主GC的機會。

7、幾種垃圾收集器

在JDK7中,有5種垃圾收集器:

  • Serial收集器
  • Parallel收集器
  • Parallel Old收集器 (Parallel Compacting GC)收集器
  • Concurrent Mark & Sweep GC (or “CMS”)收集器
  • Garbage First (G1) 收集器

其中,Serial 收集器一定不能用於伺服器端。這個收集器類型僅應用於單核CPU桌面電腦。使用Serial收集器會顯著降低應用程式的性能。


本文永久更新地址:https://github.com/nnngu/LearningNotes/blob/master/JVM/03%20JVM%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6.md


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

-Advertisement-
Play Games
更多相關文章
  • 首先繼承Thread類,然後重寫Thread類的run()方法。 Thread類的子類的對象調用start()方法,然後虛擬機就會調用該線程的run()方法。 當程式執行到start()方法時,線程啟動,此時有兩條執行路徑,一條是主方法執行main方法,另一條是線程路徑執行線程run()里的代碼,兩 ...
  • 開散列法又叫鏈地址法(開鏈法)。 開散列法:首先對關鍵碼集合用散列函數計算散列地址,具有相同地址的關鍵碼歸於同一子集合,每一個子集合稱為一個桶,各個桶中的元素通過一個單鏈錶鏈接起來,各鏈表的頭結點存儲在哈希表中。 設元素的關鍵碼為37, 25, 14, 36, 49, 68, 57, 11, 散列表 ...
  • 解法: 最開始有三種思路: 最後採用了最後一種思路 github地址:https://github.com/CyanChan/Leetcode-Record ...
  • 近期項目中,用 jenkins 熱部署 web工程時,發現工程中靜態持有的線程(將ScheduledExecutorService定時任務存儲在靜態Map中),導致不定時出現資料庫訪問事務關閉異常,如下:org.springframework.transaction.CannotCreateTran ...
  • word裡面有2張表,需要找到第二張表,並寫入execl中: 代碼如下: 運行後生成文件 roro.xlsx,內容如下: ...
  • Java內部類 一、 含義 在Java編程語言里,程式是由類(class)構建而成的。在一個類的內部也可以聲明類,我們把這 樣的類叫做內部類。 二、 作用 實現了更好的封裝,我們知道,普通類(非內部類)的訪問修飾符不能為private或protected,而內部類可以。當我們將內部類聲明為priva ...
  • 之前寫了一篇ArrayList,那麼今天就寫一篇他的姊妹篇,LinkedList。 眾所周知,ArrayList底層數據是數組,可以在O(1)的時間內get到數據,但刪除和插入就要O(n)時間複雜度。 所以出現了鏈表,鏈表可以在O(1)的時間內插入,並且不會浪費記憶體,用多少就鏈接多少即可。 我們從以 ...
  • AJAX跨域完全講解 今天在慕課網上學習了AJAX跨域完全講解: "https://www.imooc.com/learn/947" 我在收集AJAX面試題的時候其實就已經有過AJAX跨域的問題的了,當時候知道了為什麼會存在跨域,以及跨域解決的方案有哪些,今天隨著課程的學習,又加深了AJAX跨域的理 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...