C#學習筆記(十四):GC機制和弱引用

垃圾回收(GC)

垃圾回收即Garbage Collector,垃圾指的是內存中已經不會再使用的對象,經過收集釋放掉這些對象佔用的內存。html

GC以應用程序的root爲基礎,遍歷應用程序在Heap上動態分配的全部對象,經過識別它們是否被引用來肯定哪些對象是已經死亡的、哪些仍須要被使用。已經再也不被應用程序的root或者別的對象所引用的對象就是已經死亡的對象,即所謂的垃圾,須要被回收。算法

關於C#使用的垃圾回收算法能夠點擊這裏查看。spring

析構函數

析構函數會在GC執行清楚當前對象時被調用,能夠在析構函數中執行一些釋放方法。數據庫

爲了優化GC算法,微軟使用了「代」的概念,介紹以下:編程

堆裏面總共有3代。ide

譬如,當程序運行時,有對象須要存儲在堆裏面,GC就會建立第1代(假設空間大小爲256K),對象就會存儲在第0代裏面,當程序繼續運行,運行到第0代的大小不足以存放對象,這時候就就會建立第1代(假設空間爲10M),GC就會把第0代裏面的「垃圾對象」清理掉,把「活着」的對象放在第1代,這時候第0代就空了,用於存放新來的對象,當第0代滿了的時候,就會繼續執行以上操做,隨着程序的運行,第1代不能知足存放要求,這時候就會建立第2代,清理方式如上相同。函數

咱們來看一個例子:優化

 1 using System;
 2 
 3 namespace Study
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Test test = new Test();
10             
11             //對象會被分配到第 0 代
12             Console.WriteLine("test對象所在的代:" + GC.GetGeneration(test));
13             //回收對象, 這時 test 會被分配到第 1 代
14             GC.Collect();
15             Console.WriteLine("test對象所在的代:" + GC.GetGeneration(test));
16             //回收對象, 這時 test 會被分配到第 2 代
17             GC.Collect();
18             Console.WriteLine("test對象所在的代:" + GC.GetGeneration(test));
19             //回收對象, 最多隻有3個代, 因此 test 還在第 2 代
20             GC.Collect();
21             Console.WriteLine("test對象所在的代:" + GC.GetGeneration(test));
22 
23             //斷開引用, 對象會被回收
24             test = null;
25             //回收對象, test 會被回收並調用析構函數
26             GC.Collect();
27 
28             Console.Read();
29         }
30     }
31 
32     public class Test
33     {
34         ~Test()
35         {
36             Console.WriteLine("Test被回收了!");
37         }
38     }
39 }

運行結果以下:this

1 test對象所在的代:0
2 test對象所在的代:1
3 test對象所在的代:2
4 test對象所在的代:2
5 Test被回收了!

什麼時候GC

.Net什麼時候執行GC在《C#高級編程》書中也只是簡單的一句「垃圾回收會在運行庫認爲須要他時運行。」帶過,整體而言,GC運行策略已經被微軟進行優化過了,咱們不須要過多的關心便可。spa

託管對象和非託管對象

在C#中,很大部分的對象都是託管對象,託管對象的釋放直接由GC來處理,但也存在部分非託管對象,這些對象GC是不能對其進行自動回收的

非託管對象

即不受運行時管理的資源對象(如窗口句柄 (HWND)、數據庫鏈接等)。

非託管的對象以下:ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,Timer,Tooltip ,文件句柄,GDI資源,數據庫鏈接等等資源。

好比:當咱們使用一個System.IO.StreamReader的一個文件對象,必須顯示的調用對象的Close()方法關閉它,不然會佔用系統的內存和資源,並且可能會出現意想不到的錯誤。

Finalize

當咱們聲明一個析構函數時實際上編譯器就會自動添加下面的代碼:

1 ~Test()
2 {
3     try{
4         Finalize();
5     }finally{
6         base.Finalize();
7     }
8 }

若是在派生類中不存在析造函數,卻重載了基類的終結器:

1 protected override void Finalize()
2 {
3 }

垃圾回收時,GC找不到構造函數,會直接調用終結器。

若是沒有顯示釋放資源時,GC時能夠靠該方法進行隱式的釋放資源。

Dispose

相對於重寫Finalize方法,實現IDisposable接口來進行顯示的釋放資源是更好的一種方式。

咱們只須要實現IDisposable接口便可,咱們看看官網提供的常規寫法:

 1 using System;
 2 
 3 class BaseClass : IDisposable
 4 {
 5    // Flag: Has Dispose already been called?
 6    bool disposed = false;
 7 
 8    // Public implementation of Dispose pattern callable by consumers.
 9    public void Dispose()
10    { 
11       Dispose(true);
12       GC.SuppressFinalize(this);           
13    }
14 
15    // Protected implementation of Dispose pattern.
16    protected virtual void Dispose(bool disposing)
17    {
18       if (disposed)
19          return; 
20 
21       if (disposing) {
22          // Free any other managed objects here.
23          //
24       }
25 
26       // Free any unmanaged objects here.
27       //
28       disposed = true;
29    }
30 }

https://msdn.microsoft.com/zh-cn/library/system.idisposable(v=vs.110).aspx

https://msdn.microsoft.com/zh-cn/library/fs2xkftw(v=vs.110).aspx

弱引用

咱們都知道當一個對象存在一個或多個引用時,GC是不會釋放該對象的,以下:

Object obj = new Object();

這種引用稱爲強引用。

可是咱們能夠想一下,還有一種狀況是對象稍後可能被使用,但不是很肯定是否會使用時,就能夠使用弱引用了。

弱引用能夠理解爲:若是一個對象只存在一個或多個弱引用而沒有強引用時,則GC能夠對其進行垃圾回收。

那麼在C#中該如何使用弱引用呢?

WeakReference和WeakReference<T>

C#提供了兩個類來實現弱引用的功能,咱們只須要將對象裝入該類的實例中,則能夠理解爲爲這個對象添加了一個弱引用,下面給出幫助文檔地址:

WeakReference:https://msdn.microsoft.com/zh-cn/library/system.weakreference(v=vs.110).aspx

WeakReference<T>:https://msdn.microsoft.com/zh-cn/library/gg712738(v=vs.110).aspx

咱們再看一個例子:

 1 using System;
 2 
 3 namespace Study
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             //強引用
10             Test test = new Test();
11             //弱引用 1
12             WeakReference weak1 = new WeakReference(test);
13             //弱引用 2
14             WeakReference<Test> weak2 = new WeakReference<Test>(test);
15 
16             //存在強引用不會被回收
17             GC.Collect();
18 
19             //注意 temp 也是一個強引用
20             Test temp;
21 
22             Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp));
23 
24             //解除全部強引用
25             test = null;
26             temp = null;
27 
28             //沒有強引用會被回收
29             GC.Collect();
30 
31             Console.WriteLine("weak1: " + weak1.IsAlive + ", weak2: " + weak2.TryGetTarget(out temp));
32 
33             Console.Read();
34         }
35     }
36 
37     public class Test
38     {
39         ~Test()
40         {
41             Console.WriteLine("Test被回收了!");
42         }
43     }
44 }

結果以下:

1 weak1: True, weak2: True
2 weak1: False, weak2: False
3 Test被回收了!
相關文章
相關標籤/搜索