一文瞭解.Net的CLR、GC記憶體管理

来源:https://www.cnblogs.com/kawhi187/archive/2022/08/26/16626827.html
-Advertisement-
Play Games

一文瞭解.Net的CLR、GC記憶體管理 微軟官方文檔對記憶體管理和CLR的概述 什麼是托管代碼? 托管代碼就是執行過程交由運行時管理的代碼。 在這種情況下,相關的運行時稱為公共語言運行時 (CLR),不管使用的是哪種實現(例如 Mono、.NET Framework 或 .NET Core/.NET ...


  • 一文瞭解.Net的CLR、GC記憶體管理

    微軟官方文檔對記憶體管理和CLR的概述
    • 什麼是托管代碼?

      • 托管代碼就是執行過程交由運行時管理的代碼。 在這種情況下,相關的運行時稱為公共語言運行時 (CLR),不管使用的是哪種實現(例如 Mono、.NET Framework 或 .NET Core/.NET 5+)。 CLR 負責提取托管代碼、將其編譯成機器代碼,然後執行它。 除此之外,運行時還提供多個重要服務,例如自動記憶體管理、安全邊界、類型安全,等等。
      • 托管代碼是使用可在 .NET 上運行的一種高級語言(例如 C#、Visual Basic、F# 等)編寫的。 使用相應的編譯器編譯以這些語言編寫的代碼時,無法獲得機器代碼, 而是獲得 中間語言 代碼,然後運行時會對其進行編譯並將其執行。
    • 什麼是中間語言?

      • 中間語言是編譯使用高級 .NET 語言編寫的代碼後獲得的結果。對使用其中一種語言編寫的代碼進行編譯後,即可獲得 IL 所生成的二進位代碼。
      • 從高級代碼生成 IL 後,你很有可能想要運行它。 CLR 此時將接管工作,啟動 實時 (JIT) 編譯過程,或者將代碼從 IL 實時 編譯成可以真正在 CPU 上運行的機器代碼。 這樣,CLR 就能確切地知道代碼的作用,並可以有效地 管理 代碼。
      • 中間語言有時也稱為公共中間語言 (CIL) 或 Microsoft 中間語言 (MSIL)。
    • 自動記憶體管理

      自動記憶體管理是公共語言運行時在托管執行過程中提供的服務之一。 公共語言運行時的垃圾回收器為應用程式管理記憶體的分配和釋放。
      • 分配記憶體

        • 初始化新進程時,運行時會為進程保留一個連續的地址空間區域。但是這個記憶體其實是虛擬的連續記憶體的,在物理記憶體上記憶體在物理上不一定連續的。這個保留的地址空間被稱為托管堆。 托管堆維護著一個指針,用它指向將在堆中分配的下一個對象的地址。
      • 釋放記憶體(GC如何表示對象需要回收、GC執行的流程?)

        • 垃圾回收器的優化引擎根據所執行的分配決定執行回收的最佳時間。 垃圾回收器在執行回收時,會釋放應用程式不再使用的對象的記憶體。 它通過檢查應用程式的根來確定不再使用的對象,垃圾回收器需要選定一系列的起點根(root)以保證對象的遍歷,提供給垃圾回收器創建有向引用圖的根,每次回收都會從根觸發去遍歷對象圖中所有被引用的對象並標記,然後是清理和壓縮未被引用的對象。
        • 為了改進性能,運行時為單獨堆中的大型對象分配記憶體。 垃圾回收器會自動釋放大型對象的記憶體。 但是,為了避免移動記憶體中的大型對象,不會壓縮此記憶體。
    • 托管堆的級別和觸發回收的時機

      為優化垃圾回收器的性能,將托管堆分為三代:第 0 代、第 1 代和第 2 代。運行時的垃圾回收器將新對象存儲在第 0 級中。 在應用程式生存期的早期創建的對象如果未被回收,則被升級並存儲在第 1 級和第 2 級中。 GC的回收機制是通過檢查掃描根引用和標記來進行確定回收的,這個根也稱為GC根。執行第 1 代 GC 時,將同時回收第 1 代和第 0 代。 執行第 2 代 GC 時,將回收整個堆。 因此,第 2 代 GC 還可稱為“完整 GC”
      • 第 0 代執行回收的時機

        垃圾回收器在第 0 級托管堆已滿時執行回收。 如果應用程式在第 0 級托管堆已滿時嘗試新建對象,垃圾回收器將會發現第 0 級托管堆中沒有可分配給該對象的剩餘地址空間。 垃圾回收器執行回收,嘗試為對象釋放第 0 級托管堆中的地址空間。 垃圾回收器從檢查第 0 級托管堆中的對象(而不是托管堆中的所有對象)開始執行回收。
      • 第 1 代對象的創建和執行回收時機

        垃圾回收器執行第 0 級托管堆的回收後,會壓縮可訪問對象的記憶體,垃圾回收器升級這些對象,並考慮第 1 代托管堆的這一部分對象。 因為未被回收的對象往往具有較長的生存期,所以將它們升級至更高的級別很有意義。 因此,垃圾回收器在每次執行第 0 代托管堆的回收時,不必重新檢查第 1 代和第 2 代托管堆中的對象。
      • 第 2 代 對象的創建和執行回收時機

        圾回收器執行第 1 代托管堆的回收後,會壓縮可訪問對象的記憶體,垃圾回收器升級這些對象,並考慮第 2 代托管堆的這一部分對象。第 2 代托管堆中未被回收的對象會繼續保留在第 2 代托管堆中,直到在將來的回收中確定它們無法訪問為止。大型對象堆上的對象(有時稱為 第 3 代)也在第 2 代中收集。
      • 垃圾回收器的優化引擎會決定是否需要檢查較舊的級別中的對象

        如果第 0 級托管堆的回收沒有回收足夠的記憶體,不能使應用程式成功完成創建新對象的嘗試,垃圾回收器就會先執行第 1 級托管堆的回收,然後再執行第 2 級托管堆的回收。 如果這樣仍不能回收足夠的記憶體,垃圾回收器將執行第 2、1 和 0 級托管堆的回收。 每次回收後,垃圾回收器都會壓縮第 0 級托管堆中的可訪問對象並將它們升級至第 1 級托管堆。 第 1 級托管堆中未被回收的對象將會升級至第 2 級托管堆。 由於垃圾回收器只支持三個級別,因此第 2 級托管堆中未被回收的對象會繼續保留在第 2 級托管堆中,直到在將來的回收中確定它們為無法訪問為止。
      • 非托管資源的記憶體釋放

        對於應用程式創建的大多數對象,可以依賴垃圾回收器自動執行必要的記憶體管理任務。 但是,非托管資源需要顯式清除。 最常用的非托管資源類型是包裝操作系統資源的對象,例如,文件句柄、視窗句柄或網路連接。 雖然垃圾回收器可以跟蹤封裝非托管資源的托管對象的生存期,但卻無法具體瞭解如何清理資源。 創建封裝非托管資源的對象時,建議在公共 Dispose 方法中提供必要的代碼以清理非托管資源。 通過提供 Dispose 方法,對象的用戶可以在使用完對象後顯式釋放其記憶體。
    • 85kb的劃分是指淺層對象還是深層對象?

      假如我們現在有一個對象Order對象如下定義,那麼我們的order是應該在第0代還是在大對象堆呢?
      答案是在第0代,但是OrderItems是在大對象堆中的,所以85kb的只計算了淺層對象的大小,不計算對象的深層大小。
      public class Progarm{
        public static void main(){
          var order=new Order();// 0代
        }
      }
      public class Order {
        public OrderItem OrderItems{get private set;}=[5000];//>85kb位元組 3代
      }
      public class OrderItem{
        public string ProductId{get; private set;}
      }
      
    • 垃圾回收

      • 基礎

        • 優點

          • 開發人員不必手動釋放記憶體。
          • 有效分配托管堆上的對象。
          • 回收不再使用的對象,清除它們的記憶體,並保留記憶體以用於將來分配。 托管對象會自動獲取乾凈的內容來開始,因此,它們的構造函數不必對每個數據欄位進行初始化。
          • 通過確保對象不能使用另一個對象的內容來提供記憶體安全。
        • 垃圾回收的條件

          • 系統具有低的物理記憶體。 這是通過 OS 的記憶體不足通知或主機指示的記憶體不足檢測出來。
          • 由托管堆上已分配的對象使用的記憶體超出了可接受的閾值。 隨著進程的運行,此閾值會不斷地進行調整。
          • 調用GC.Collect方法。 幾乎在所有情況下,你都不必調用此方法,因為垃圾回收器會持續運行。 此方法主要用於特殊情況和測試。
        • 垃圾回收過程中會有哪些操作?

          在垃圾回收啟動之前,除了觸發垃圾回收的線程,其它以外的所有托管線程都會被掛起。下圖微軟官方文檔演示了觸發垃圾回收並導致其他線程掛起的線程。
          • 標記階段,找到並創建所有活動對象的列表。
          • 重定位階段,用於更新對將要壓縮的對象的引用。
          • 壓縮階段,用於回收由死對象占用的空間,並壓縮幸存的對象。 壓縮階段將垃圾回收中幸存下來的對象移至段中時間較早的一端。因為第 2 代回收可以占用多個段,所以可以將已提升到第 2 代中的對象移動到時間較早的段中。 可以將第 1 代幸存者和第 2 代幸存者都移動到不同的段,因為它們已被提升到第 2 代。
          • 垃圾回收器使用以下信息來確定對象是否為活動對象:
            • 堆棧根。 由實時 (JIT) 編譯器和堆棧查看器提供的堆棧變數。 JIT 優化可以延長或縮短報告給垃圾回收器的堆棧變數內的代碼的區域。
            • 垃圾回收句柄。 指向托管對象且可由用戶代碼或公共語言運行時分配的句柄。
            • 靜態數據。 應用程式域中可能引用其他對象的靜態對象。 每個應用程式域都會跟蹤其靜態對象。
      • CLR中垃圾回收的分類

        從 .NET Framework 4.5 開始,後臺垃圾回收可用於伺服器 GC。 伺服器 GC 是伺服器垃圾回收的預設模式。後臺工作區域垃圾回收使用一個專用的後臺垃圾回收線程,而後臺伺服器垃圾回收使用多個線程。 通常一個邏輯處理器有一個專用線程。不同於工作站後臺垃圾回收線程,這些後臺伺服器 GC 線程不會超時。

      • 工作站垃圾回收

        工作站垃圾回收 (GC) 是為客戶端應用設計的。 它是獨立應用的預設 GC 風格。對於托管應用(例如由 ASP.NET 托管的應用),由主機確定預設 GC 風格。工作站垃圾回收既可以是併發的,也可以是非併發的。 併發(或後臺 )垃圾回收使托管線程能夠在垃圾回收期間繼續操作。後臺垃圾回收替換 .NET Framework 4 及更高版本中的並行垃圾回收。 工作站垃圾回收使用用於只有一個處理器的電腦。

      • 伺服器垃圾回收

        • 伺服器垃圾回收主要用於需要高吞吐量和可伸縮行的伺服器應用程式例如(WebApi)這種,在.Net Core和Framework4.5之後的版本中,伺服器垃圾回收既可以是非併發也可以是後臺執行的。
        • 服務垃圾回收會為每個CPU提供一個用於執行垃圾回收的一個堆和專用線程,並能同時回收這些堆。每個堆都包含一個小對象堆和一個大對象堆,並且所有的堆都可由用戶代碼訪問。 不同堆上的對象可以相互引用。
        • 因為多個垃圾回收線程一起工作,所以對於相同大小的堆,伺服器垃圾回收比工作站垃圾回收更快一些。
        • 伺服器垃圾回收通常具有更大的段。 但是,這是通常情況:段大小特定於實現且可能更改。 調整應用程式時,不要假設垃圾回收器分配的段大小。
        • 伺服器垃圾回收會占用大量資源。 例如,假設在一臺有 4 個處理器的電腦上,運行著 12 個使用伺服器 GC 的進程。 如果所有進程碰巧同時回收垃圾,它們會相互干擾,因為將在同一個處理器上調度 12 個線程。 如果進程處於活動狀態,則最好不要讓它們都使用伺服器 GC。
      • 並行垃圾回收

        • 併發垃圾回收通過最大程度地減少因回收引起的暫停,使交互應用程式能夠更快地響應。 在運行併發垃圾回收線程的大多數時間,托管線程可以繼續運行。 此設計使得在發生垃圾回收時的暫停時間更短。
        • 併發垃圾回收在一個專用線程上執行。 預設情況下,CLR 將運行工作站垃圾回收,併在單處理器和多處理器電腦上同時啟用併發垃圾回收。
      • 前臺和後臺的垃圾回收區別

        垃圾回收分為前臺和後臺所謂的後臺回收是指 (gen2這一代所需要回收的對象還有大對象堆,大對象堆不會compact)那麼前臺回收說的就是gen0和gen1這一代所需要回收的對象了。

        • 前臺垃圾回收

          發生前臺垃圾回收的時候所有的托管線程會處於掛起階段,也就是說這個時候是GC線程執行的階段,其他線程不處理任務。另外就是前臺的垃圾回收也是由後臺垃圾回收線程去執行的,它是這麼做的如果後臺垃圾回收的線程在檢測是否有前臺發起垃圾回收指令,如果收到了就掛起後臺的垃圾回收的線程,執行前臺的垃圾回收的線程。在前臺垃圾回收完成之後,專用的後臺垃圾回收線程和用戶線程將繼續。
        • 後臺垃圾回收

          後臺垃圾回收可以消除併發垃圾回收所帶來的分配限制,因為在後臺垃圾回收期間,可發生暫時垃圾回收。 後臺垃圾回收可以刪除暫存世代中的死對象。如果需要,它還可以在第1代垃圾回收期間擴展堆。
    • 大對象堆(LOH)和小對象堆分別是什麼?

      載入 CLR 時,GC 分配兩個初始堆段:一個用於小型對象(小型對象堆或 SOH),一個用於大型對象(大型對象堆)。通過將托管對象置於這些托管堆段上來滿足分配請求。 如果該對象小於 85kb,則將它置於 SOH 的段上,否則,將它置於 LOH 段。 觸發垃圾回收後,GC 將尋找存在的對象並將它們壓縮。 但是由於壓縮費用很高,GC 會掃過 LOH,列出沒有被清除的對象列表以供以後重新使用,從而滿足大型對象的分配請求。 相鄰的被清除對象將組成一個自由對象。用戶代碼只能在第 0 代(小型對象)或 LOH(大型對象)中分配。只有 GC 可以在第 1 代(通過提升第 0 代回收未處理的對象)和第 2 代(通過提升第 1 代和第 2 代回收未處理的對象)中“分配”對象。
      • .Net GC將需要回收的對象分為了大對象堆和小對象堆,如果是大對象的話那麼它的某些特性比小對象顯得更為重要,例如複製一個對象到記憶體堆的其他位置的性能消耗會相當高,因此GC 會直接將大對象放置到大對象堆上面,大對象和小對象的區分預設是以85kb來作為區分,這個數字是可改的。
      • 小對象一般是指第0代和第1代中的對象,這類對象在GC 回收的時候執行效率會很高,所以為了優化GC 回收器的性能所以才會區分代數,大多數對象都會通過第 0 代GC 回收進行回收,所以不會保留到下一代。大對象堆的回收時機是隨著2代GC 回收而執行,2代GC也是完整GC。
      • 大對象堆在什麼情況會啟動垃圾回收?

        • 分配超出第0代或者大對象閥值。
        • 調用GC.Collect方法。
        • 系統處於記憶體不足的狀況下同樣也會回收。
      • 為什麼會存在大對象堆?

        • 分配成本。
        • 回收成本。
        • 具有引用類型的數組元素。
    • GC / IDisposable / 析構函數三者的關係?

      • GC是負責管理托管資源的記憶體的,它負責不存在引用地址對象的記憶體。
      • 析構函數和IDisposable的介面的區別:析構是沒有執行順序的,析構函數的執行時機無法確定,所以官方給提供了IDisposable的介面,讓開發人員可以在代碼中顯示的調用Dispose釋放方法。
    • 記憶體溢出如何發現和解決?

學不完的技術,寫不完的代碼。QQ群:773595012
▼-----------------------------------------▼-----------------------------------------▼
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • SQL與Pandas都可以完成大部分數據分析需求。本文用SQL與Pands逐一實現10類核心數據分析需求,輕鬆進行對比學習:數據選擇、限制、統計計數、排序、新欄位生成、數據選擇、數據分組、統計均值、方差、極差/範圍。 ...
  • 在開發過程中經常會遇到 MD5、SHA1、SHA256 等詞語,這些是加密演算法嗎?嚴格意義上講,這些並不是加密演算法,而是消息摘要演算法。咱就用人聽得懂的話來聊聊“消息摘要”。 ...
  • “請你描述一下Redis的緩存淘汰策略” 你如果你正好遇到這個問題,想好怎麼回答了嗎? 關於這個問題,我把高手的回答整理到了15W字的面試文檔裡面 大家可以私信留言領取。 下麵看看高手的回答。 高手: 這個問題我需要從三個方面來回答。 第一個方面: 當Redis使用的記憶體達到maxmemory參數配 ...
  • 1.用 __new__方法 class Singleton(object): def __new__(cls): if not hasattr(cls,'_instance'): cls._instance=super(Singleton,cls).__new__(cls) # cls.__inst ...
  • 1. Durid概述 Apache Druid是一個集時間序列資料庫、數據倉庫和全文檢索系統特點於一體的分析性數據平臺。本文將帶你簡單瞭解Druid的特性,使用場景,技術特點和架構。這將有助於你選型數據存儲方案,深入瞭解Druid存儲,深入瞭解時間序列存儲等。 Apache Druid是一個高性能的 ...
  • 大家好,我是字母哥(coder)! 我讓公司的小伙伴寫一個生產級別的PostgreSQL的安裝文檔,結果他和我說:“不是用一個命令就能安裝好麽?還用寫文檔麽?”。我知道他想說的是這個命令:yum install postgresql-server,我也是挺無語的。要知道生產級別的應用安裝方式,和自己 ...
  • 作者:小牛呼嚕嚕 | https://xiaoniuhululu.com 電腦內功、JAVA底層、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕 」 序列化和反序列化的概念 當我們在Java中創建對象的時候,對象會一直存在,直到程式終止時。但有時候可能存在一種"持久化"場景:我們需要讓對象能夠在程 ...
  • 概述 本文描述WPF的拖放功能(Drag and Drop)。 拖放功能涉及到兩個功能,一個就是拖,一個是放。拖放可以發生在兩個控制項之間,也可以在一個控制項自己內部拖放。假設界面上有兩個控制項,一個TreeView,一個ListView,那麼可能發生的拖動有以下幾種情況: 1、TreeView -> L ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...