1.無論怎樣盡力,我們總是會使用到某些需要大量內存的數據,而這些內存並不需要經常訪問。或許你需要從一個大文件中查找某個特定的值,或者算法需要一個較大的查詢表。這時,你也許會采用2中不太好做法:第一種是創建一個本地變量,然後在每次執行該算法時都生成一大塊垃圾;第二種則是創建一個成員變量,在很長一段時間內都占用著這一大塊內存。很多時候這兩種做法都不是非常好。
2.有沒有一種好的選擇來處這個問題呢?答案是創建弱引用,弱引用的對象和垃圾對象差不多,程序會告訴垃圾收集器該對象可以被回收,不過在回收之前你仍舊有一個引用,可以用來在需要的時候訪問到該對象。若能合理使用該策略,弱引用將和垃圾收集器協同工作,從而優化內存使用。如下代碼說明:
/// <summary> /// 大對象類型 /// </summary> public class MyLargeClass { private int[,] matrix; private ushort matrixXDimension; private ushort matrixYDimension; public string reallyLongMessage; private static WeakReference _weakObj; public static object WeakObj { get { return _weakObj?.Target;//“?.”語法為C#6.0的新特性,對應.Net Framework 4.6。表示若為空則返回空,若不為空則獲取其相應的屬性值 } set { _weakObj = new WeakReference(value);//弱引用的創建 } } public MyLargeClass(ushort matrixXDimension, ushort matrixYDimension) { this.matrixXDimension = matrixXDimension; this.matrixYDimension = matrixYDimension; matrix = new int[matrixXDimension, matrixYDimension]; } private void Initialize() { //TODO 做更多的數據初始化 //對matrix二維數組的數據加載 } public long Calculate() { //TODO 處理對matrix對象計算結果 return matrixXDimension * matrixYDimension * 1000; } public static void Execute() { //從弱引用中獲得大對象,若沒有弱引用或弱引用已被回收,此時就需要重新創建對象。 MyLargeClass myLargeObject = WeakObj as MyLargeClass;//myLargeObject可為本地變量,也可為成員變量 if (myLargeObject == null) { myLargeObject = new MyLargeClass(1000, 1000); } //TODO 對myLargeObject對象中的數據進行處理 //處理完成後,把大對象歸到弱引用,然後把myLargeObject設置為null,表明大對象無引用。此時,系統會認為myLargeObject所指向的對象可以被垃圾收集。若垃圾收集器在此時運行,那麼將會回收該對象。不過若是在回收之前,你再次需要該對象,只需如上面的4行代碼處理即可。 WeakObj = myLargeObject; myLargeObject = null; } }
3.從上面的Execute()方法的2處代碼中,可以看出對象的使用與清理交給了運行時處理,這是簡單的使用場景,不過很多時候現實並不是那麼簡單,很少有對象會完全與外界隔離。任何一個大對象都包含了對其他對象的引用。若一個大對象被標記為弱引用,則其內部引用對象也將被標記為弱引用。被標記的弱引用對象就會被回收(不是馬上回收)。
4.對於實現了IDisposable的對象,則不應該使用弱引用,因為被釋放的對象已經不能再弱引用,且在弱引用中你也不能調用Dispose()。所以弱引用應該配合非IDisposable的大對象使用。