C# 記憶體的理解 通俗說

不三週助發表於2019-06-21

一.概念

  堆疊是什麼?

  在說堆疊之前,先說說記憶體是神馬?

  記憶體:程式在執行的過程,電腦需要不斷通過CPU進行計算,這個計算的過程會讀取併產生運算的資料,這些資料需要一個儲存容器存放。這個容器,這就是記憶體了。

  我們知道C#是強型別語言,每個變數和常量都有一個型別,即所有的資料都會有一個型別。在.Net中,所有的型別又分為值型別和引用型別。簡單介紹一下。

    值型別:使用int,float,struct,enum關鍵字直接繼承自System.ValueType定義的型別。

    引用型別:Class,interface,string,delegate繼承自System.Object。

  值型別和引用型別有什麼區別吶?

  主要區別在於值型別物件固定大小,引用型別物件可以指向任何型別,無法確定其大小。因此記憶體區域分為棧和堆。值型別儲存在棧上,引用型別儲存在堆上。

二.棧和堆

                   

 

      棧:作業系統會為每條執行緒分配一定的空間,Windows為1M。在棧上的成員不受GC管理器的控制,直接由作業系統分配。或者理解為儲存短期較小資料塊,超出作用域,自動釋放。

      堆:主要用來存放引用型別物件,不需要我們人工去分配和釋放,由GC管理器託管。或者理解為儲存長期較大資料塊,超出作用域並不會被釋放,保持被分配的狀態。GC會處理未引用的堆記憶體。

      不同於值型別直接在棧中存放值,引用型別還需要在棧中存放一個指向堆中物件的值的地址。

      值型別,引用型別區別詳見:https://www.cnblogs.com/u3ddjw/p/6756536.html

 

      值型別和引用型別之間可以互轉嗎?

         這個需求,是很常見,所以要說一下。答案是肯定的,當然可以,這裡就需要提到裝箱(值型別===>引用型別),拆箱(引用型別===>值型別)。

          裝箱:

              I.分配堆記憶體

              II.將值型別的例項欄位拷貝到新分配的堆記憶體中

              III.返回託管堆中新分配的物件的地址在棧中。這個地址就是一個指向物件的引用。

          拆箱:

              I.檢查物件例項,確保它是給定值型別的一個裝箱值。

              II.將該值例項複製到值型別變數中。

            

三.GC垃圾管理器

   ①GC和堆記憶體聯絡

    上述說到棧是作業系統實時自動分配釋放,不需要我們去管理。堆記憶體也是由GC控制管理。但是GC並不是實時管理的,是需要通過程式設計師手動或系統定時觸發的。因為GC是一個耗時的操作,可能在有些系統中觸發的不合時宜(明顯示卡頓)。所以,GC也需要優化,需要控制在合事宜的情況觸發。比如遊戲中我們需要在切換Loading時觸發GC,而在遊戲戰鬥中控制不能被觸發。

     因此優化GC,就是優化堆記憶體,就是儘量減少堆記憶體,及時回收堆記憶體。

   ②GC是什麼?

      GC即(Gabarage Collector,垃圾回收器),歸屬於CLR(公共語言執行時,可以理解為.Net虛擬機器),專門用於回收託管堆記憶體的

    ③GC如何釋放堆記憶體的?

    GC清理堆時,GC收集器會通過一定的演算法清理堆中的物件,並且版本不同演算法也不同。標記-壓縮演算法:通過一個圖的資料結構來收集物件的根,這個根就是引用地址。可以理解為指向託管堆的關係線。當觸發這個演算法時,會檢查圖中的每個根是否可達,如果可達,則對其標記,然後在堆上找到剩餘沒有標記的物件進行刪除,這樣,

那些不再使用的堆中物件就刪除了。

    為了優化記憶體結構,減少在圖中搜尋的成本,GC機制又為每個託管堆物件定義了一個屬性,將每個物件分為三個等級,0代,1代,2代。

每當new一個物件的時候,該物件會被定義為第0代,當GC開始回收的時候,先從第0代開始,在這樣一次回收動作之後,0代沒有被回收的物件則被定義為第1代,當回收第1代的時候,第1代中沒有被清理的物件會被定義為第2代。

    CLR會為0/1/2代選擇一個預算的容量,0代通常為256k-4mb預算,1代為512-4m,2代不受限制,最大可擴充至作業系統的整個記憶體空間。代數越長說明這個物件經歷了回收的次數越多,那就意味著該物件是最不容易被清除的。這種分代的思想將物件分割成新老物件,進而配對不同的清除條件,這種巧妙的思想避免了直接清理整個堆(卡頓後果)。   

    

擴充套件說明:

      比如Unity採用貝姆垃圾回收機制與.Net垃圾回收器相比一直有很大的限制。

        I.貝姆垃圾回收:無分代\並行,執行時所有執行緒阻塞;每次標記都會訪問所有可達的物件(窮舉搜尋垃圾)。這種方式極有可能在短時間造成幀率下降,影響玩家體驗。

        II.分代回收:效率高很多。

 

        參考:https://1996v.cnblogs.com/p/9037603.html?from=timeline&isappinstalled=0 

相關文章