.Net Discovery系列之四 深入理解.Net垃圾收集機制(下)

上一節給大家介紹了 .Net GC的運行機制,下面來講下與GC相關的重要方法。

第二節.GC關鍵方法解析

1.Dispose()方法

Dispose可用于釋放所有資源,包括托管的和非托管的,需要自己實現。

大多數的非托管資源都要求手動釋放,我們應當為釋放非托管資源公開一個方法,實現釋放非托管資源的方法有很多種,實現IDispose接口的Dispose方法是最好的,這可以給使用你類庫的程序員以明確的說明,讓他們知道怎樣釋放你的資源;而且C#中用到的using語句快,也是在離開語句塊時自動調用Dispose方法。

這里需要注意的是,如果基類實現了IDispose接口,那么它的派生類也必須實現自己的IDispose,并在其Dispose方法中調用基類中Dispose方法。只有這樣的才能保證當你使用派生類實例后,釋放資源時,連同基類中的非托管資源一起釋放掉。

插曲:使用using與try+finally的區別

可以說2者沒有任何區別,因為using只是編輯器級的優化,它與try+finally有著相同的作用,以下是一段使用using的代碼,它在IL階段也是以try+finally呈現的:

C#:

public partial class _Default : System.Web.UI.Page
    {
     protected void Page_Load(object sender, EventArgs e)?
     {
       using (DataSet ds = new DataSet())
       {
        }
     }
   }

   MSIL:
   .method family hidebysig instance void  Page_Load(object sender,class [mscorlib]System.EventArgs e) cil managed
   {
    // 代碼大小       29 (0x1d)
    .maxstack  2
    .locals init ([0] class [System.Data]System.Data.DataSet ds,
             [1] bool CS$4$0000)
    IL_0000:  nop
    IL_0001:  newobj     instance void [System.Data]System.Data.DataSet::.ctor()
    IL_0006:  stloc.0
    .try
    {
      IL_0007:  nop
      IL_0008:  nop
      IL_0009:  leave.s    IL_001b
    }  // end .try
    finally
    {
      IL_000b:  ldloc.0
      IL_000c:  ldnull
      IL_000d:  ceq
      IL_000f:  stloc.1
      IL_0010:  ldloc.1
      IL_0011:  brtrue.s   IL_001a
      IL_0013:  ldloc.0
      IL_0014:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
      IL_0019:  nop
      IL_001a:  endfinally
    }  // end handler
    IL_001b:  nop
    IL_001c:  ret
   } // end of method _Default::Page_Load

但是,using的優點是,在代碼離開using塊時,using會自動調用Idispose接口的Dispose()方法。

?

2. GC.Collect()方法

如果我們在程序中顯式的調用了垃圾收集器的collect接口,那么垃圾收集器會立即運行,完成內存對象的標記、壓縮與清除工作,使用GC.Collect(i)還可以指定回收的代,然而aicken并不贊成各位同學顯式調用它:

⑴. GC.Collect()做的并不只是回收內存,就像第一節中介紹的,在回收了內存之后,GC會重新整理內存,修正對象指針,讓空閑內存連續,供CLR順序分配內存,提高新建對象的效率。內存壓縮整理工作非常耗用計算資源。

⑵.很少有人會關心到GC除了在內存吃緊以及資源空閑時運行,還會在什么時候運行。 其實GC的運行時機,還要受到一個叫做“策略引擎”的部件控制,它會觀察GC的收集頻率、效率等等。它會根據GC回收效果,調整GC運行的頻率:即當某次GC回收效果頗豐時,它便會增加GC運行的頻率,反之亦然。

所以如果剛剛發生了一次自然的收集,垃圾對象就會非常之少,而此時程序又顯式的進行了收集調用,那么自然, GC雖然小有收獲,但是策略引擎就會認為:這很不值得,才收集了這么點垃圾,也許該減少GC的次數。這樣一來,垃圾收集器努力保持的自然節奏就被打亂了。

同時,對象類型的創建效率與頻率,也會被“策略引擎”捕捉到,從而改變代的數量與容量。

所以,額外的調用GC,代價高昂,甚至會降低效率。顯示的調用GC.Collect(),實質是在用“時間換空間”,而通常在程序設計中,我們推薦的設計原則是“空間換時間”,比如使用各種各樣的緩存。

也有例外,如果你掌握了整個應用程序的情況,明確的知道何時會產生大量垃圾,也是可以顯示調用該方法的。

綜上,盡量不要顯示調用GC.Collect(),因為服務器的CPU比內存要貴的多!

3. 析構函數(Finalize())

我們知道,GC只負責釋放托管資源,非托管資源GC是無法釋放的。類似文件操作、數據庫連接等都會產用非托管資源。

Finalize方法是用于釋放非托管資源的,等同于C#中是析構函數,C#編譯器在編譯構造函數時,會隱式的將析構函數編譯為Finalize()對應的代碼,并確定在finally塊中執行了base.Finalize()。

析構函數中只能釋放非托管資源,而不要在任何托管資源進行析構,原因如下:

⑴你無法預測析構函數的運行時機,它不是按順序執行的。當析構函數被執行的時候,也許你進行操作的托管資源已經被釋放了。

⑵包含Finalize()的對象,需要GC的兩次處理才能刪除。

⑶CLR會在單獨的線程上執行所有對象的Finalize()方法,無疑,如果頻繁的Finalize(),會降低系統的性能。

下面我們來重點說說第⑵點,為何包含Finalize()的對象,需要兩次GC才能被清除。

首先要了解與Finalize相關的兩個隊列:終止隊列(Finalization Queue)與可達隊列(Freachable Queue),這兩個隊列存儲了一組指向對象的指針。

當程序中在托管堆上分配空間時(new),如果該類含有析構函數,GC將在Finalization Queue中添加一個指向該對象的指針。

在GC首次運行時,會在已經被確認為垃圾的對象中遍歷,如果某個垃圾對象的指針被Finalization Queue包含,GC將這個對象從垃圾中分離出來,將它的指針儲存到Freachable Queue中,并在Finalization Queue刪除這個對象的指針記錄,這時該對象就不是垃圾了——這個過程被稱為是對象的復生(Resurrection)。當Freachable Queue一旦被添加了指針之后,它就會去執行對象的Finalize()方法,清除對象占用的資源。

當GC再次運行時,便會再次發現這個含有Finalize()方法的垃圾對象,但此時它在Finalization Queue中已經沒有記錄了(GC首次運行時刪掉了它的Finalization Queue記錄),那么這個對象就會被回收了。

至此,通過GC兩次運行,終于回收了帶有析構函數的對象。

?

復活實例:

private void Form1_Load(object sender, EventArgs e)?
   {
     Resource re = new Resource();
     re = null;GC.Collect();
     GC.WaitForPendingFinalizers();
     //首次GC.Collect()沒起作用哦。?
     label1.Text = re.num.ToString();
   }
   public class Resource
  {
   public int num;
     ~Resource()
     {
       。。。
     }
   }

看了上面的代碼,大家應該了解什么是復活了吧!那么為什么要復生呢?因為首次GC時,這個對象的Finalize()方法還沒有被執行,如果不經過復生就被GC掉,那么就連它的Finalize()一起回收了,Finalize()就無法運行了,所以必須先復生,以執行它的Finalize(),然后再回收。

還有兩個方法ReRegisterForFinalize和SuppressFinalize需要講一講,ReRegisterForFinalize是將指向對象的指針重新添加到Finalization Queue中(即召喚系統執行Finalize()方法),SuppressFinalize是將對象的指針從Finalization Queue中移除(即拒絕系統執行Finalize()方法)。

SuppressFinalize用于那些即有析構函數來釋放資源,又實現了Dispose()方法釋放資源的情況下:將GC.SuppressFinalize(this)添加至Dispose()方法中,以確保程序員調用Dispose()后,GC就不必再次收集了,例如以下代碼:

public class Resource : Idisposable
   {
      private bool isDispose = false;
      //實現Dispose(),后面還有析構函數,以防程序員忘記調用Dispose()方法
      public void Dispose() {
      Dispose(true);
       GC.SuppressFinalize(this);
    }
   protected virtual void Dispose(bool disposing)
   {
    if (!isDispose)
    {
     if (disposing)
     {
      //清理托管資源
     }
     //清理非管資源
    }
    isDispose = true;
   }
   ~ Resource ()
   {
    Dispose(false);
   }
  }

即實現Idisposable中的Dispose()方法,又使用析構函數,一個雙保險,大家不要迷惑,其實在釋放非托管資源時,使用一個即可,推薦使用前者。

4.弱引用(WeakReference)

最后一個話題:弱引用。在編程中,對于那些大對象建議使用這種引用方式,這種引用不影響GC回收:我們用過了某個對象,然后將其至null,這樣GC就可以快速回收它了,但是沒過多久我們又需要這個對象了,沒辦法,只好重新創建實例,這樣就浪費了創建實例所需的計算資源;而如果不至null,就會浪費內存資源。對于這種情況,我們可以創建一個這個大對象的弱引用,這樣在內存不夠時GC可以快速回收,而在沒有被GC回收前我們還可以再次利用該對象。

public class SomeObject?
   {
     。。。
   }

   public static void Main()?
   {
      SomeObject so = new SomeObject();
      WeakReference WRso = new WeakReference(so);
     so = null;
      Console.WriteLine(WRso.IsAlive); // True
      // 調用GC 手動回收。
      GC.Collect();
      Console.WriteLine(WRso.IsAlive); // False

   }

看到沒,在so = null;后,它的弱引用依然是可用的。所以對于大對象的使用,aicken建議使用此種方式。另外,弱引用有長短之分:長弱引用在對象終結后,依然追蹤對象;短弱引用則反之,aicken不建議人為干預GC的工作成果,所以推薦使用短弱引用,即上面代碼中的方式。

通過以上的講解,相信大家已經能夠很全面的了解.Net GC方面的知識了。

?

轉自:http://www.cnblogs.com/isline/archive/2009/03/04/1402713.html

轉載于:https://www.cnblogs.com/zjoch/p/5237455.html

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

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

相關文章

真靜態和偽靜態的區別

首先肯定的是純靜態和偽靜態都是SEO的產物,但純靜態和偽靜態還是有很大區別的。 純靜態是生成真實的HTML頁面保存到服務器端,用戶訪問時直接訪問這 個HTML頁面即可,從而大大的減輕了服務器壓力(如dedecms就是采用的純靜態&#xf…

非常有趣的Console

console覺醒之路,打印個動畫如何? 原文地址: http://www.helloweba.com/view-blog-383.html 批量去掉或替換文本中的換行符(notepad、sublime text2) 原文地址:http://m.blog.csdn.net/article/details?id43228729 有…

shopee蝦皮科技測試工程師第一次筆試

10道單選題 10道多選題 2道編程題 第一題:十進制轉二進制計算1的個數(負數轉為補碼) #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2021/8/23 15:44 # Author : linlianqin # Site : # File : 十進制轉換為二進制&am…

假期實踐

第一天 地點:杭州頤高數碼城 第一天,我來到了自己家附近的頤高數碼城。文三路這邊有一個賣數碼產品的一條街,這里也是最貼近我專業實踐的地方,所以第一天的實踐我選擇了這里。 2001年開業的頤高數碼廣場座落于“電子一條街”文三路、學院路口…

3.AngularJS-過濾器

轉自:https://www.cnblogs.com/best/p/6225621.html 二、過濾器 使用過濾器格式化數據,變換數據格式,在模板中使用一個插值變量。語法格式如下: {{ express | filter:parameter1:p2:p3… | … | …}} 過濾器分了內置過濾器與自定義…

webstorm卡頓問題

解決webstorm卡頓問題 webstorm強大的功能就不多做介紹了。但是它的缺點也顯而易見:吃內存。 電腦配置稍低一點,運行webstorm就特別容易卡頓,特別是項目比較大的時候,那卡頓得不要不要的。 在我的筆記本8g內存 256ssd的配置下&…

cmd.exe啟動參數說明

啟動命令解釋程序 Cmd.exe 的新范例。如果在不含參數的情況下使用,cmd 將顯示操作系統的版本和版權信息。 語法 cmd [{/c | /k}] [/s] [/q] [/d] [{/a | /u}] [/t:FG] [/e:{on | off}] [/f:{on | off}] [/v:{on | off}] [String] 參數 /c 執行 String 指定的命令&am…

【深度學習】——訓練過程

包含哪些層 訓練過程 其實就是yf(x)的求參過程,先給參數一個初始值,然后根據初始函數計算得到預測值,根據預測值和真值計算損失,然后又根據損失函數進行反向傳播更新參數,更新參數后,再次計算預測值&#…

ABB RAPID 程序 WorldZone 歸納

在 RAPID 程序中,靜態的 WorldZone 不能被解除并再次激活,或者進行擦除。在 RAPID 程序中, 臨時的 WorldZone 可以被解除(WZDisable) , 再次激活(WZEnable) 或者擦除(WZF…

thinkphp自定義模板標簽(一)

thinkphp內置的foreach和include等模板標簽使用是非常方便的;但是內置的那些標簽只能滿足常用功能,個性化的功能就需要我們自己編寫自定義模板標簽了;下面就是要講解如何實現; 示例環境:thinkphp3.2.3 thinkphp的模板標…

【深度學習】——激活函數(sigmoid、tanh、relu、softmax)

目錄 激活函數 1、作用 2、常用激活函數 3、衡量激活函數好壞的標準: 4、不同的激活函數 1)sigmoid 2)tanh函數 3)RULE函數和leak-relu函數 4)softmax函數 激活函數 1、作用 如果只是線性卷積的話&#xff0c…

SDUT 3377 數據結構實驗之查找五:平方之哈希表

數據結構實驗之查找五:平方之哈希表 Time Limit: 400MS Memory Limit: 65536KBSubmit StatisticProblem Description 給定的一組無重復數據的正整數,根據給定的哈希函數建立其對應hash表,哈希函數是H(Key)Key%P,P是哈希表表長&…

我的2017年前端之路總結

原文首發于我的博客 年末了,趕著剛考完兩門考試,在最后4門考試來臨之前抽空寫一下今年的小結。 今年格外忙。忙完本科畢設,又馬上投入了研究生實驗室的搬磚生涯。跟去年一樣,列個今年的學習成果清單: 過去的一年 技術成…

對軟件工程的疑問

在大學時光中學習了算法編程后,我發現我對于源程序理解很差,我只會很低程度的寫代碼,但是基本描述不出來。所以我的編程很差,而且由于我很少打代碼,所以我的編程能力基本沒有多少提高,我也沒有發現該學什么…

【深度學習】——分類損失函數、回歸損失函數、交叉熵損失函數、均方差損失函數、損失函數曲線、

目錄 代碼 回歸問題的損失函數 分類問題的損失函數 1、 0-1損失 (zero-one loss) 2、Logistic loss 3、Hinge loss 4、指數損失(Exponential loss) 機器學習的損失函數 Cross Entropy Loss Function(交叉熵損失函數) 交叉熵優點 Mean Squared E…

伺服電機慣量問題

在伺服系統選型及調試中,常會碰到慣量問題。 其具體表現為:在伺服系統選型時,除考慮電機的扭矩和額定速度等等因素外,我們還需要先計算得知機械系統換算到電機軸的慣量,再根據機械的實際動作要求及加工件質量要求來…

【轉】應用架構一團糟?如何將單體應用改造為微服務

概述 將單體應用改造為微服務實際上是應用現代化的過程,這是開發者們在過去十年來一直在做的事情,所以已經有一些可以復用的經驗。 全部重寫是絕對不能用的策略,除非你要集中精力從頭構建一個基于微服務的應用。雖然聽起來很有吸引力&#xf…

Linux 解決ssh連接慢的問題

備份文件 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak 編輯文件 vi /etc/ssh/sshd_config 輸入/ 查找GSSAPIAuthentication 設置如下 GSSAPIAuthentication no # 是否允許使用基于 GSSAPI 的用戶認證。默認值為"no"。僅用于SSH-2 詳細解釋 輸入/ 查找UseDNS …

ABB機器人與PC計算機控制口連接 超級終端 命令清單

條件: 9 針串口通信 RS232。 PC 啟動超級終端軟件。Windows -> Start -> Accessories -> Terminal 通信設置: 1. 波特率 9600 8 位2. 1 個停止位 沒有奇偶校驗3. 沒有 Modern 采用直接串口連接4. 使用 Xon/Xoff 通信形式當故障發生時&#xff0…

【Hibernate】Hibernate系列6之HQL查詢

HQL查詢 6.1、概述 6.2、分頁查詢 6.3、命名查詢 6.4、投影查詢-部分字段查詢 6.5、報表查詢 6.6、迫切左外連接、左外連接 6.7、迫切內連接、內連接 6.8、QBC查詢、本地查詢 轉載于:https://www.cnblogs.com/junneyang/p/5254641.html