程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> 對象的生與死(優化內存)

對象的生與死(優化內存)

編輯:C#基礎知識
基礎才是重中之重~對象的生與死

回到目錄

為何要寫

之所以寫這篇文章,完全是因為學生們在實際開發中遇到的問題,一個對象占用的內存空間總不被釋放,導致系統內存不斷攀升,其最主要原因是我們對“對象的生與死”不清楚,或者從來沒有認真去考慮過這件事,確實一個對象在被聲音,初始化,使用或者最後被系統回收,整個的過程與我們關系確實不大,我們開發人員直接用就行了,對於C#這種托管語言你沒必要去自己回收它,但有時,我們多了解一點系統的回收機制,對我們的程序還是很有好處的。

對象的種類(根據作用域)

1 類對象,靜態對象,使用static修飾符進行聲明,作用域為整個類(所有實體公有),當你的程序運行期間它一直不會被回收,直到你的進程結束(所以使用它要注冊,大數據一般不用類對象存儲)

2 類級別的實例對象,它定義在類裡,方法體外面,作用域為整個類的當前實例,它的回收時機我們無法確定,當然你可以手動進行  GC.Collect()來馬上釋放它,否則由系統的垃圾回收機制管理它

3 方法級別的實例對象,局部對象,它定義在方法內部,作用域為當前方法體,方法執行完成後,自動釋放

代碼中的實驗

    public class Product
    {
        ~Product()
        {
            Logger.LoggerFactory.Instance.Logger_Info("Product對象已經被釋放");
        }
        public int ID { get; set; }
        public string Name { get; set; }
    }

    public class ProductConfig
    {
        ~ProductConfig()
        {
            Logger.LoggerFactory.Instance.Logger_Info("ProductConfig對象已經被釋放");
        }
        public int ID { get; set; }
        public string Name { get; set; }
    }
        /// <summary>
        /// 類級別的
        /// </summary>
        ProductConfig productConfig = new ProductConfig();
        public ActionResult Create()
        {
            /// <summary>
            /// 實例成員,方法體裡
            /// </summary>
            Product product = new Product();
            GC.Collect();//清除全局實例成員,否則全局實例對象將不會馬上清楚,它會等待垃圾回收
            return View();
        }

上面代碼在執行後,會寫入日志文件,由於在方法裡使用了GC.Collect()方法,這時全局實例對象ProductConfig將會在方法執行後被釋放,如果不加這個方法,那麼ProductConfig何時釋放,我們是不知道的。

Dispose模式

對上面的操作是我們刻意去進行的,意思就是讓大家看到,對象何時會被釋放,而對象在被釋放後,會執行類的析構方法(~開頭的),它在C#裡很少被使用,或者我們很少關注它,因為你不去實現它,系統垃圾回收結束後也會去調用它,這是對於托管資源說的,我們在C#這個開發語言裡,有時也會涉及到使用一些“非托管”資源,比如數據庫連接,網絡通訊,文件訪問等等,它們是不受當前Frameworks CLR控制的,或者說CLR也控制不了它,因為它已經脫離了當前應用程序,這也算是合情合理,這時,這些非托管資源會實現自己的“資源釋放”方法,好Dispose,大家如果有心的話,都會發現像文件,SQL連接,socket,Tcp等對象,都有Dispose方法,它的意思就是釋放當前對象,而我們在使用它們時,如何把非托管與托管對象結合起來,一起把對象釋放呢,這就是現在要說的Dispose模式

   /// <summary>
    /// 實現IDisposable,對非托管系統進行資源回收
    /// 作者:倉儲大叔
    /// </summary>
    public abstract class DisposableBase : IDisposable
    {
        /// <summary>
        /// 標准Dispose,外界可以直接調用它
        /// </summary>
        public void Dispose()
        {
            Logger.LoggerFactory.Instance.Logger_Debug("Dispose");

            this.Dispose(true);////釋放托管資源
            GC.SuppressFinalize(this);//請求系統不要調用指定對象的終結器. //該方法在對象頭中設置一個位,系統在調用終結器時將檢查這個位
        }

        private void Dispose(bool disposing)
        {
            if (!_isDisposed)//_isDisposed為false表示沒有進行手動dispose
            {
                //清理托管資源和清理非托管資源
                Finalize(disposing);
            }
            Logger.LoggerFactory.Instance.Logger_Debug("Dispose complete!");
            _isDisposed = true;
        }

        /// <summary>
        /// 由子類自己去實現自己的Dispose邏輯(清理托管和非托管資源)
        /// </summary>
        /// <param name="disposing"></param>
        protected abstract void Finalize(bool disposing);

        private bool _isDisposed;

        /// <summary>
        /// 是否完成了資源的釋放
        /// </summary>
        public bool IsDisposed
        {
            get { return this._isDisposed; }
        }
        /// <summary>
        /// 析構方法-在類被釋放前被執行
        /// </summary>
        ~DisposableBase()
        {
            Logger.LoggerFactory.Instance.Logger_Debug("析構方法");

            //執行到這裡,托管資源已經被釋放
            this.Dispose(false);//釋放非托管資源,托管資源由終極器自己完成了
        }
    }

使用它

  public class ZzlTools : DisposableBase
    {

        protected override void Finalize(bool disposing)
        {
            if (!disposing)
            {
                //清除托管
            }
            //清理非托管
        }
    }

通過大叔整理的Dispose基類,我們可以看到,外界的對象只要實現Finalize方法即可,把自己需要釋放的對象寫在Finalize裡就行了,簡單!

最後,和大家分享我的一個經驗,學習基礎知識,就像修煉內功,我們一定要打好根基,才能更上一層樓!

謝謝閱讀!

回到目錄 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved