.NET中的GC垃圾回收


托管堆垃圾回收--CLR提供GC。

1、什么樣的對象需要垃圾回收?

  托管資源+引用類型

  托管資源和非托管資源:

    托管的就是CLR控制的,例如:new的對象、string字符串、變量等;

    非托管不是CLR能控制的,例如:數據庫連接、文件流、句柄、打印機連接等;

    using(SqlConnection)--被C#封裝了用來管理那個非托管的數據庫連接資源;

    只要是需要手動釋放的,都是非托管的。

2、哪些對象的內存,能被GC回收?

  對象訪問不到了,那就可以被回收了

  程序--入口--去找對象--建立對象圖--訪問不到的就是垃圾。

3、對象是如何分配在堆上?

  連續分配在堆上面,每次分配就先檢查空間夠不夠。

4、什么時候執行GC?

  a、new對象時--臨界點

  b、GC.Collect 強制GC

  c、程序退出時會GC

  a=null

  GC.Collect  可以GC,但是頻繁GC是不好的,GC是全局的。

  如果項目中有6個小時才運行new一次,什么時候GC? 這6個小時都不GC,可以手動GC。

5、GC的過程是怎么樣的呢?

  N個對象--全部對象標記為垃圾--入口開始遍歷--訪問到的就標記可以訪問(+1)--遍歷完就清理內存--產生不連續內存--壓縮--地址移動--修改變量指向--所以會全局阻塞。

  清理內存分2種情況:

    a、無析構函數,直接清理內存。

    b、把對象轉移到一個單獨的隊列,會有個析構器線程專門做這個(清理慢一些),析構函數內部通常是用來做非托管資源釋放的,因為CLR肯定會調用,可以避免使用者忘記的情況。

6、垃圾回收策略

  對象分代:3代

  0代:第一次分配到堆,就是0代

  1代:經歷了一次GC,依然還在的

  2代:經歷了兩次或以上GC,依然還在的

  垃圾回收時,優先回收0代,提升效率,最多也最容易釋放。

  0代不夠---找1代---1代不夠才找2代--再不夠就不夠了。。。

  大對象堆,沒有分代,直接都是2代,80000字節以上就叫大對象。

  大對象堆之所以沒有分代這主要有2個原因:一是內存移動時如果是大對象則很耗資源;二是0代空間問題;

7、標准Dispose模式

using System;

namespace MyGC
{
    /// <summary>
    /// 標准Dispose模式
    /// 
    /// 析構函數:被動清理
    /// Dispose:主動清理
    /// </summary>
    public class StandardDispose : IDisposable
    {
        /// <summary>
        /// 演示創建一個非托管資源
        /// </summary>
        private string _unmanageResource = "未被托管的資源";

        /// <summary>
        /// 演示創建一個托管資源
        /// </summary>
        private string _manageResource = "托管的資源";

        /// <summary>
        /// 是否已釋放
        /// </summary>
        private bool _disposed = false;

        /// <summary>
        /// 實現IDisposable中的Dispose方法
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true); //必須為true
            GC.SuppressFinalize(this); //通知垃圾回收機制不再調用終結器(析構器)(析構函數)
        }

        /// <summary>
        /// 不是必要的,提供一個Close方法僅僅是為了更符合其他語言(如C++)的規范
        /// </summary>
        public void Close()
        {
            this.Dispose();
        }

        /// <summary>
        /// 析構函數
        /// 必須,以備程序員忘記了顯式調用Dispose方法
        /// </summary>
        ~StandardDispose()
        {
            //必須為false
            this.Dispose(false);
        }

        /// <summary>
        /// 非密封類修飾用protected virtual
        /// 密封類修飾用private
        /// </summary>
        /// <param name="disposing">是否清理托管資源</param>
        protected virtual void Dispose(bool disposing)
        {
            if (this._disposed) //避免已經被釋放的拋異常
            {
                return;
            }

            if (disposing)
            {
                //清理托管資源
                if (this._manageResource != null)
                {
                    //Dispose
                    this._manageResource = null;
                }
            }

            //清理非托管資源
            if (this._unmanageResource != null)
            {
                //Dispose  conn.Dispose()
                this._unmanageResource = null;
            }

            //讓類型知道自己已經被釋放
            this._disposed = true;
        }

        public void PublicMethod()
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException("StandardDispose", "StandardDispose is disposed");
            }
        }
    }
}

PS

托管資源的釋放一般都是由CLR去保證的,在實現IDisposable接口的Dispose方法里面之所以手動的去釋放托管資源是為了限時清理,速度快點。

而在析構函數里面不去清理托管資源是因為這個時候托管資源會進入隊列,不確定回收的時機。

 

此文由博主精心撰寫轉載請保留此原文鏈接:https://www.cnblogs.com/xyh9039/p/13715181.html

版權聲明:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM