【Go專家編程——內存管理——垃圾回收】

垃圾回收

所謂的垃圾就上不在需要的內存塊,垃圾如果不清理,這些內存塊就沒有辦法再次被分配使用。在不支持垃圾回收的編程語言中,這些垃圾內存就上泄露的內存。

1. 垃圾回收算法

常見的垃圾回收算法有3種

  • 引用計數:對每個對象維護一個引用計數,當引用該對象的對象被銷毀時,引用計數減1,當引用計數器為0時回收該對象
    • 優點:對象可以很快被回收,不會出現內存耗盡或達到某個閾值時才回收
    • 缺點:不能很好地處理循環引用,實施維護引用計數也有一定的代價
    • 代表語言:Python,PHP,Swift
  • 標記-清除:從根變量開始遍歷所有引用的對象,引用的對象標記為"被引用",沒有標記的對象被回收
    • 優點:解決了引用計數的缺點
    • 缺點:需要STW,即暫停程序運行
    • 代表語言:Go(三色標記法)
  • 分代收集:按照對象聲明周期的長短劃分不同的代空間,生命周期長的放入老年代,短的放入新生代,不同代有不同的回收算法和回收頻率
    • 優點:回收性能好
    • 缺點:算法復雜
    • 代表語言:Java

2.Go垃圾回收

2.1 垃圾回收的原理

垃圾回收的核心就是標記處那些內存還在使用,哪些內存不再使用了(即未被引用),把未被引用的內存回收,以供后續內存分配使用。

垃圾回收開始時從root對象掃描,把root對象引用的內存標記為“被引用”,考慮到內存塊中存放的可能是指針,所以還需要遞歸地進行標記,全部標記完成后,只保留被標記的內存,未被標記的內存全部標記為未分配即完成了回收。

2.2 內存標記(Mark)

之前的博客有介紹了span數據結構,span中維護了一個個內存塊,并有成員變量allocBits表示每個內存塊的分配情況。在span的數據結構中還有另一個位圖gcmarkBits(之前的文章中未被寫出),用于標記內存塊被引用的情況。

allocBits和gcmarkBits的數據結構完全一樣,標記結束后就上內存回收,回收時將allocBits指向gcmarkBits,代表標記過的內存才是存活的內存,gcmarkBits則會在下次標記時重新分配內存。

2.3 三色標記法

三色對應了垃圾回收過程中對象的三種狀態

  • 灰色:對象還在標記隊列中等待
  • 黑色:對象已被標記,gcmarkBits對應的位為1(該對象不會再本次GC中被清理)
  • 白色:對象未被標記,gcmarkBits對應的位為0(該對象會在本次GC中被清理)

在這里插入圖片描述

  1. 初始化階段:
    • 所有對象最初都被標記為白色,表示尚未訪問。
    • 設置一個根集,根集通常包括當前的棧變量、全局變量以及Go運行時的數據結構等可以直接訪問到的對象。這些根集中的對象被標記為灰色。
  2. 并發標記階段:
    • 從灰色對象開始,垃圾回收器會遍歷這些對象引用的所有對象,并將其從白色改為灰色,表示這些對象已被發現但其引用的對象還未被檢查。
    • 同時,標記過程會遞歸進行,每當完成一個對象的引用檢查(即將其引用的所有白色對象標記為灰色),該對象就會被標記為黑色,表示該對象及其所有引用鏈上的可達對象都已被訪問過,不會再被重新訪問。
    • 這個過程是并發執行的,即垃圾回收器與程序的其他部分并行工作,以減少停頓時間。
  3. 重新掃描(重新標記)階段:
    • 由于標記階段是并發進行的,程序可能在此期間修改了某些對象的引用關系,這可能導致一些本應標記為灰色或黑色的對象仍保持為白色。因此,需要有一個階段來修正這種不一致,這個階段稱為重新掃描或重新標記階段。
    • 在這個階段,垃圾回收器會暫停所有非垃圾回收相關的任務,再次檢查灰色對象,并確保它們的引用關系已經被正確處理,新發現的白色對象會被標記為灰色繼續處理,直至沒有灰色對象剩余。
  4. 清理階段:
    • 當所有可達對象都被標記為黑色后,垃圾回收器知道所有白色對象都是不可達的,可以被安全地回收。
    • 這個階段會釋放那些白色對象占用的內存空間,為新的分配做準備。

STW(Stop Whe World)就是停止所有的goroutine,專心做垃圾回收,待垃圾回收結束后再回復goroutine
STW時間的長短直接影響了應用的執行。

3. 垃圾回收優化

為了縮短STW的時間,Go也在不斷地優化垃圾回收算法。

3.1 寫屏障(Write Barrier)

  • 寫屏障就是讓goroutine與GC同時運行的手段,寫屏障可以打打縮短STW的時間。
  • 寫屏障類似一種開關,在GC的特定時機開啟,開啟后指針傳遞時會標記指針,即本輪不回收,下次GC時再確定。
  • GC過程中新分配的內存會被立即標記,用的正是寫屏障技術,即GC過程中分配的內存不會再本輪GC中回收

3.2 輔助GC(Mutator Assist)

為了防止內存分配過快,在GC執行過程中,如果goroutine需要分配內存,那么該goroutine會參與一部分GC的工作,即幫組GC做一部分工作,這個機制叫做Mutator Assist。

4. 垃圾回收的觸發時機

4.1 內存分配量達到閾值觸發GC

每次內存分配時都會檢查當前內存分配量是否已達到閾值,如果達到閾值則立即啟動GC。
閾值 = 上次GC內存分配量 * 內存增長率
內存增長率由環境變量GOGC控制,默認為100,即每當內存擴大一倍時啟動GC

4.2 定期觸發GC

默認情況下,最長2分鐘觸發一次GC。
通過變量forcegcperiod變量中被聲明

4.3 手動觸發

程序代碼中也可以使用使用runtime.GC()來手動觸發GC,主要用于GC的性能測試和統計。

5. GC性能優化

  • GC性能與對象數量負相關,對象越多GC性能越差,對程序影響越大。
  • 所以GC性能優化的思路之一就是減少對象分配的個數:比如使用對象復用或使用大對象組合多個小對象
  • 內存逃逸現象會產生一些隱式的內存分配,也有可能成為GC的負擔

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/15467.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/15467.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/15467.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

yolov10 快速使用及訓練

參考: https://docs.ultralytics.com/models/yolov10/ ultralytics其實大多數系列都能加載使用: 官方: https://github.com/THU-MIG/yolov10.git 代碼參考: https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/train-yolov10-object-…

一篇文章講透排序算法之希爾排序

希爾排序是對插入排序的優化,如果你不了解插入排序的話,可以先閱讀這篇文章:插入排序 目錄 1.插入排序的問題 2.希爾排序的思路 3.希爾排序的實現 4.希爾排序的優化 5.希爾排序的時間復雜度 1.插入排序的問題 如果用插入排序對一個逆序…

521源碼-免費代碼基礎學習-PHP如何運用變量教程

更多網站源碼學習教程,請點擊👉-521源碼-👈獲取最新資源 為什么要學習PHP?“我可以用JavaScript來實現程序編寫。”但JavaScript的能力是有限的,JavaScript通常運行在瀏覽器(客戶端)&#xff0…

go語言中for的4種循環形式總結

和其他語言不一樣,go語言中的循環語句只有for一種,但是go里面的for卻有3種不同的循環形式,總結如下: 1. 無限循環 for { //這個就是一個“死循環”,注意必須要有 break條件,否則就真成死循環了 } 2. fo…

Redis 源碼學習記錄:集合 (set)

無序集合 Redis 源碼版本:Redis-6.0.9,本篇文章無序集合的代碼均在 intset.h / intset.c 文件中。 Redis 通常使用字典結構保存用戶集合數據,字典鍵存儲集合元素,字典值為空。如果一個集合全是整數,則使用字典國語浪費…

PostgreSQL入門教程

PostgreSQL是一種開源的關系型數據庫管理系統,它具有高度的可靠性、可擴展性和性能。下面是一個簡單的PostgreSQL入門教程,幫助你開始使用這個強大的數據庫管理系統。 步驟1:安裝PostgreSQL 首先,你需要下載并安裝PostgreSQL。你…

2024年最全的信息安全、數據安全、網絡安全標準分享(可下載)

以上是資料簡介和目錄,如需下載,請前往星球獲取:https://t.zsxq.com/Gz1a0

【全網最全】2024電工杯數學建模A題成品論文+前三題完整解答matlab+py代碼等(后續會更新成品論文)

您的點贊收藏是我繼續更新的最大動力! 一定要點擊如下的卡片鏈接,那是獲取資料的入口! 【全網最全】2024電工杯數學建模A題成品論文前三題完整解答matlabpy代碼等(后續會更新成品論文)「首先來看看目前已有的資料&am…

Python 點云平面分割【RANSAC算法】

點云平面分割 一、介紹1.1 概念1.2 算法思路1.3 參數設置二、代碼示例三、結果示例其他參考鏈接:C++中實現點云平面分割 一、介紹 1.1 概念 點云平面分割:可以在點云數據中找到平面并計算平面模型系數,同時輸出平面點云及非平面點云。 1.2 算法思路 實現思路: 首先,采用…

Sass是什么?有哪些優缺點?

目錄 一、Sass是什么? 二、Sass的優缺點 三、Sass與SaaS 一、Sass是什么? Sass是世界上最成熟、最穩定、最強大的專業級CSS擴展語言。 Sass makes CSS fun again. Sass is an extension of CSS, adding nested rules, variables, mixins, selector in…

【C++高階(一)】繼承

目錄 一、繼承的概念 1.繼承的基本概念 2.繼承的定義和語法 3.繼承基類成員訪問方式的變化 ?編輯 4.總結 二、基類和派生類對象賦值轉換 三、繼承中的作用域 四、派生類的默認成員函數 1.派生類中的默認構造函數 2.派生類中的拷貝構造函數 3.派生類中的移動構造函數…

英語學習筆記25——Mrs. Smith‘s kitchen

Mrs. Smith’s kitchen 史密斯太太的廚房 詞匯 Vocabulary Mrs. 夫人【已婚】 復習:Mr. 先生 全名 / 姓    Mrs. 夫人 全名 / 丈夫的姓    Miss 小姐(未婚) 全名 / 姓    Ms. 女士 全名 / 姓 查看婚姻狀況,可以觀察…

springboot項目中圖片上傳之后需要重啟工程才能看到圖片?

需求背景 最近在做一個用戶自定義上傳頭像的小需求,用戶上傳頭像然后需要立馬回顯。 需求是很常見的、正當的需求。如果不使用到對象存儲這類服務,我們把用戶頭像的圖片文件僅存在本地就可以了。我們在開發的過程中為了工程管理方便通常下意識會將圖片…

freertos串口DMA隊列發送卡死

調試回調函數的時候,我在cube中刪除了默認的DMA通道,又新增了另外一個通道,導致NVIC中,該通道的優先級為0,后來改成了5就正常了。

Modbus TCP轉Profinet網關測試配置案例

本案例采用XD-ETHPN20網關做為Modbus TCP通信協議設備與Profinet通信協議設備連接的橋梁。Modbus TCP是一種基于TCP/IP協議的工業通信協議,而Profinet則是用于太網通信的協議。Modbus TCP轉Profinet網關可實現這兩種不同協議之間的數據交換和傳輸,極大地…

算法刷題筆記 逆序對的數量(C++實現)

文章目錄 題目描述解題代碼&#xff08;蠻力版&#xff09;解題代碼&#xff08;基于歸并排序&#xff09; 題目描述 給定一個長度為n的整數數列&#xff0c;請你計算數列中的逆序對的數量。逆序對的定義如下&#xff1a;對于數列的第i個和第j個元素&#xff0c;如果滿足i<…

Python高級進階--dict字典

dict字典?? 1. 字典簡介 dictionary&#xff08;字典&#xff09; 是 除列表以外 Python 之中 最靈活 的數據類型&#xff0c;類型為dict 字典同樣可以用來存儲多個數據字典使用鍵值對存儲數據 2. 字典的定義 字典用{}定義鍵值對之間使用,分隔鍵和值之間使用:分隔 d {中…

【ECharts】數據可視化

目錄 ECharts介紹ECharts 特點Vue2使用EChats步驟安裝 ECharts引入 ECharts創建圖表容器初始化圖表更新圖表 示例基本柱狀圖后臺代碼vue2代碼配置 組件代碼運行效果 基本折線圖示例代碼組件 基礎餅圖示例代碼后臺前端配置組件運行效果 其他 ECharts介紹 ECharts 是一個由百度開…

spring模塊(一)容器(4)ApplicationContextAware

一、介紹 1、問題引入 為了獲取已被實例化的Bean對象,如果使用再次加載配置文件的方法,可能會出現一個問題,如一些線程配置任務, 會啟動兩份,產生了冗余. ApplicationContext appContext new ClassPathXmlApplicationContext("applicationContext.xml"); UserS…

python 多線程處理圖片

thread for i in range(len(ori_path)):for filename in os.listdir(ori_path[i]):number_img number_img 1print("正在處理第" str(number_img) "張圖片")img_name ori_path[i] filenamet Thread(target deal_one_img, args [img_name, filenam…