JVM 垃圾回收機制

来源:https://www.cnblogs.com/hujingnb/archive/2019/01/05/10181620.html
-Advertisement-
Play Games

首先JVM的記憶體結構包括五大區域: 程式計數器、虛擬機棧、本地方法棧、方法區、堆區。其中程式計數器、虛擬機棧和本地方法棧3個區域隨線程啟動與銷毀, 因此這幾個區域的記憶體分配和回收都具有確定性,不需要過多考慮回收的問題。而Java堆區和方法區則不一樣,這部分記憶體的分配和回收是動態的,正式垃圾回收需要關 ...


首先JVM的記憶體結構包括五大區域: 程式計數器、虛擬機棧、本地方法棧、方法區、堆區。其中程式計數器、虛擬機棧和本地方法棧3個區域隨線程啟動與銷毀, 因此這幾個區域的記憶體分配和回收都具有確定性,不需要過多考慮回收的問題。而Java堆區和方法區則不一樣,這部分記憶體的分配和回收是動態的,正式垃圾回收需要關註的部分。

垃圾回收在堆記憶體進行回收前, 要先確定區域的哪些對象是可以被回收的、那些對象暫時還不能回收,下麵談一談判斷對象是否存活的演算法。

判斷對象是否存活的演算法

1.引用計數演算法

引用計數演算法:堆中的每個對象實例都有一個引用計數器,當一個對象被創建時,就將該對象實例分配給一個變數,該引用計數器設置為1,當任何其他變數被賦值為這個對象的引用時,計數加1,當一個對象實例的某個引用超過了生命周期或被賦為一個新值時, 引用計數減1。

任何引用計數器為0的對象實例都可以進行垃圾回收。當一個對象實例被垃圾回收時,它引用的所有對象實例引用計數器減1.

優點:引用計數器可以很快的執行,對程式不需要長時間的打斷

缺點:無法檢測出迴圈引用。如對象A有對象B的引用,對象B又有對象A的引用,這樣他們的引用計數永遠都不為0

2.可達性分析演算法

可達性演算法:將所有的引用關係看作一張圖,從一個節點GC Root開始,尋找對應的引用節點,找到後繼續尋找這個節點的引用節點,當所有引用節點尋找完畢後,剩餘的節點就被認為是沒有被引用的節點,即無用節點,無用節點被判定為可回收對象。

Java中可以作為GC Root的包括下麵幾種:

  1. 虛擬機棧中的引用對象
  2. 方法區中類靜態屬性引用的對象
  3. 方法區中常量引用的對象
  4. 本地方法棧中引用的對象

對於Java中的引用類型可以看這篇文章Java 控制類的引用類型,合理使用記憶體

常用的垃圾回收演算法

1.標記-清除演算法

標記-清除演算法採用從根集合(GC Roots)進行掃描,對存活的對象進行標記,標記完畢後,再掃描整個空間中未被標記的對象,進行垃圾回收

這種演算法實現起來比較容易,但是會造成記憶體碎片

2.標記-複製演算法

複製演算法是為瞭解決標記-清除演算法的缺陷而提出的。

它將記憶體劃分為大小相等的兩塊,每次只使用其中的一塊。當這A快記憶體用完了,就將還存活的對象複製到B塊上面,然後把A塊的記憶體空間一次性清理掉

這種演算法雖然實現簡單,運行高效且不易產生記憶體碎片,但是卻對記憶體空間的使用做出了高昂的代價,因為能使用的空間縮減為原來的一半。很顯然,複製演算法的效率跟存活對象的數量有很大關聯,若存活對象很多,那麼效率將大大降低

3.標記-整理演算法

該演算法是為瞭解決複製演算法的缺陷,充分利用記憶體空間而提出的。

該演算法與標記-清除演算法一樣,但是在完成標記後,不直接清理可回收對象,而是將存活對象全部向一端移動,接著清理掉邊界以外的記憶體。

4.分代收集演算法

分代收集演算法是目前大部分JVM的垃圾收集器採用的演算法。其核心思想是根據對象存活的生命周期將記憶體劃分為若幹個不同的區域。

將其分為年輕代、老年代和永久代。然後根據不同的區域採用合適的收集演算法。

Java一般將堆區分為年輕代和老年代,將方法區劃為永久代。

下麵對不同的年齡代進行簡單說明

年輕代:新創建的對象都存放在這裡。因為年輕代會頻繁的進行GC清理,JVM在年輕代採用的是標記-複製演算法,先標記出存活的實例,然後清除掉無用實例,將存活的實例根據年齡(每個實例被經歷一次GC後年齡會加1)拷貝到不同的年齡代。

老年代:老年代中是經歷了N此垃圾禍首後仍然存活的對象,其中的N由JVM的參數決定。這塊記憶體區域一般大於年輕代。GC發生的次數也比年輕代要少。

永久代:用於存放靜態文件,如Java類、方法等。為方法區。

方法區主要回收的內容有:廢棄的常量、無用的類,對與廢棄常量可以同過引用的可達性判斷,但是對於無用類需要同時滿足以下3個條件:

  1. 該類的所有實例都已經被回收了
  2. 載入該類的 ClassLoader 已經被回收了
  3. 該類對應的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法

GC在什麼時候觸發

GC在優先順序最低的線程中運行,一般在應用程式空閑時被調用。當記憶體不足時才會主動調用

因為對象進行了分代處理,因此垃圾回收區域、時間也不一樣。GC有如下兩種:

1.Scavenge GC

一般情況下,當新對象生成,並且在年輕代申請空間失敗時,會觸發Scavenge GC, 對年輕代進行垃圾回收。這種方式的GC不會影響到老年代。因為大部分對象都是年輕代開始的,同時年輕代記憶體不會分配的很大,所有年輕代的GC會頻繁的進行。所以在這裡要使用速度快、效率高的演算法,使其空間儘快空出來。

若GC一次後仍不能滿足記憶體分配,JVM會進行二次GC,若仍無法滿足,則報“out of memory"的錯誤,Java應用將停止

2.Full GC

對整個記憶體進行整理,包括年輕代、老年代和永久代,所以Full GC比Scavenge GC要慢, 因此應該儘量減少Full GC的次數。以下可能引發Full GC的原因:

  1. 老年代被寫滿
  2. 永久代被寫滿
  3. System.gc()被顯示調用
  4. 上一次GC後堆的各域分配策略動態變化。

Java的垃圾回收介紹到這,下麵在說說如何在程式中減少GC的開銷的幾個建議:

  1. 不要顯式調用System.gc()。此函數建議JVM進行GC,雖然只是建議,但是大多數情況下會觸發GC,增加了間歇性停頓的次數,大大影響系統的性能
  2. 儘量減少臨時對象的使用。也就是減少Scavenge GC執行的機會
  3. 對象不用時最好顯式置為null。將不用的對象置為null,有利於GC收集器判定,從而提高GC的效率
  4. 儘量減少靜態對象變數。靜態變數屬於全局變數,不會被GC禍首。
  5. 能有基本類型的就不要用包裝類。基本類型變數棧用的記憶體資源比對應的包裝類要少的多
  6. 使用StringBuffer 而不是String類累加字元串。因為堆String類型進行加的時候,會創建新的String對象,而StringBuffer是可變長的,在原有基礎上進行擴增,不會產生中間對象
  7. 分散對象創建或刪除的時間。集中在短時間內大量創建新對象,特別是大對象,會突然需要大量記憶體,JVM在面臨這種情況時只能進行GC,以回收記憶體或整合記憶體碎片,從而增加GC的頻率。集中刪除對象,道理也是一樣的。它使得突然出現了大量的垃圾對象,空閑空間必然減少,從而大大增加了下一次創建新對象時強制主GC的機會。

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

-Advertisement-
Play Games
更多相關文章
  • 一、動態放入後臺的值 將'應發'下的鍵數據${key}放入html中,記得使用轉義字元 `` ,這個符號實在鍵盤左上角的第二列第一個,來表示裡面寫的是html格式的代碼。 然後 ${this.data.data['應發'][i][key]}是遍歷每行的key對應的值, 然後再把html給放入你的ht ...
  • function createUploadForm(fileElementId, data, curFileList) { var id = new Date().getTime(); var formId = 'jUploadForm' + id; //給form添加一個獨一無二的id var f... ...
  • 前兩天寫程式,有一個是對後臺返回的json數據進行整理然後再使用,用到了關於json 的拷貝。我在我的一篇博客中提到過對數組的拷貝。分為深度拷貝,和淺拷貝。這裡附上鏈接 其實對於數組的拷貝是比較簡單的,大不了就自己迴圈一下,然後放到一個新數組中,或者是在我博客中提到的使用 slice(0) 的辦法。 ...
  • 一、背景 為了達到創1的結課要求,並且實現以前就憧憬過的網站想法,我在什麼都沒有準備的情況下開始了建設網站的腳步。腦袋一熱就行動,也許我就這樣子的莽撞,造成我後來撞得滿頭包。 二、預備知識 開始的時候我只有學了一個學期的c語言,java,還有正在學習數據結構,可是對我建設網站沒有什麼幫助。不過當時粗 ...
  • 個人博客原文: "迪米特法則" 設計模式六大原則之五:迪米特法則。 簡介 姓名 :迪米特法則 英文名 :Law of Demeter 小名 :最少知識原則 小名英文名 :Least Knowledge Principle 價值觀 :媽媽說不和陌生人說話 個人介紹 : 1. Each unit sho ...
  • 系統介紹: 1.系統採用主流的 SSM 框架 jsp JSTL bootstrap html5 (PC瀏覽器使用) 2.springmvc +spring4.3.7+ mybaits3.3 SSM 普通java web(非maven, 附贈pom.xml文件) 資料庫:mysql 3.開發工具:my ...
  • 當對象之間存在一對多的關係時,若需要進行對象之間的通知,則可使用觀察者模式 介紹 觀察者模式屬於行為型模式,當一個對象的狀態發生改變時,若我們想通知其他對象,此時可通過觀察者模式來進行解決。 類圖描述 代碼實現 1、定義抽象觀察者 2、定義觀察者管理類 3、定義具體觀察者 4、上層調用 總結 觀察者 ...
  • (1)首先進入cmd,輸入pip install yagmail (2)思路:1 、連接伺服器:yagmail.SMTP(郵箱賬號,郵箱密碼,郵箱伺服器地址,郵箱伺服器埠) 2 、準備正文內容:contents="XXXXXXXX" 3 、發送郵件:yag.send(收件人列表,郵件主題,郵件內容 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...