C#內存釋放(垃圾回收)

問題背景----html

今天寫了個很小的程序,程序的功能僅僅是截圖,可是若是長時間開啓並截圖的時候,程序會變的很大,從剛開始的運行在任務管理器中只有十幾K大小,運行一段時間後在任務管理器中看到程序能夠達到1G或2G甚至更大;最初想到的是全部的截圖都保存在內存中,沒有釋放形成的。去檢查代碼,發現程序中已經使用GC.Collect();可是爲何程序仍是會一直增長呢?因爲程序中邏輯判斷等比較多,不方便跟蹤及查找。因此我本身單獨寫了個測試程序,去看看調用GC.Collect();釋放的問題?網絡

測試環境----併發

首先準備一個對象(因爲程序中使用了一些靜態變量),因此準備的對象以下:模塊化

public class CountObject
    {
        public static int Count = 0;
        public CountObject()
        {
            Count++;
        }

        ~CountObject()
        {
            Count--;
        }
    }

程序很簡單,只有一個靜態的計數變量。下面在看看主程序:性能

 1 static void Main(string[] args)
 2         {
 3             CountObject obj;
 4             for (int i = 0; i < 5; i++)
 5             {
 6                 obj = new CountObject();
 7                 //obj = null; // 這一步,只是爲了更清晰些驗證引用的對象是否釋放!
 8                 GC.Collect();
 9 
10             }
11             //GC.Collect();
12             //GC.WaitForPendingFinalizers();
13 
14             // Count不會是1,由於Finalizer不會立刻被觸發,要等到有一次回收操做(GC.Collect())後纔會被觸發。 GC.Collect();GC.WaitForPendingFinalizers();
15             Console.WriteLine(CountObject.Count);
16             Console.ReadKey();
17         }

程序也比較簡單,我作了以下測試:測試

1)使用以上程序運行,發現15行會輸出5,說明咱們調用了GC.Collect();但程序並無執行釋放,由於查GC的官方解釋,是不肯定的某個時刻進行回收。優化

2)把循環每次增大5個。當循環增長到125的時候,屢次執行後發現,我本機測試,在第15行的輸出是1或125,當增長到10000,每次都輸出1,說明符合官方解釋;this

根據以上代碼測試知道,當循環5次的時候,GC並不會當即執行,因此當執行5次循環的時候第8行沒起做用。既然不起做用,咱們把他註釋暫時不用,把11和12行開啓。spa

3)把地8行註釋,11,12行開啓,執行5次循環,發現15行輸出1,屢次執行結果相同。.net

4)再把11行註釋,12行開啓,執行5次循環,發現15行輸出5,屢次執行結果相同。

5)再把12行註釋,11行開啓,執行5次循環,發現15行輸出5,屢次執行結果相同。

根據4和5的才測試能夠看到,當少許的循環時Finalizer不會立刻被觸發,要等到有一次回收操做(GC.Collect())執行後纔會被觸發。因此咱們能夠顯式調用 GC.Collect();GC.WaitForPendingFinalizers();這兩行代碼進行強制回收的執行。

6)驗證,把第7行開啓,執行測試第15行爲0,說明對象若是沒有任何的引用則能夠強制回收。

以上是本人的一些測試,若是你還有更好的想法,能夠提出一塊兒討論;

版權歸我的全部,轉載請註明出處;

 


 

由內存釋放致使的問題:

軟件在測試力度加大狀況下,可能致使的內存不足及崩潰的問題可能快速暴露,針對這些問題能夠經過下面方式解決,歡迎補充。
1. 經常使用方式:
A)類文件中佔用內存較大的全局變量,公共變量,類私有變量及類的實例用完以後手動設置爲null或Dispose(),對局部變量不須要置null,但局部的實例須要Dispose或置null。
B)佔用內存較大的變量或實例,在循環建立這些類或實例的地方適當進行置null或Dispose()後進行GC.Collect();
 
2. 結合代碼業務進行代碼重構:
A) 將主程序中的功能模塊化,如封裝到動態庫中後,經過訂閱的方式再也不進行主動的業務請求,下降主進程負擔。
B) 對程序中會頻繁重複使用的類如心跳,網絡監控和彈出窗體,歷史信息類等,避免重複實例化,經過定義全局惟一靜態變量的方式即單例模式實現循環使用。
C) 優化代碼或重構
 
結論,一般合理使用方式1基本能夠解決大部份內存不足致使的崩潰問題,但垃圾回收有時效性等底層判斷機制,主動垃圾回收對於內存快速消耗的狀況可能效果很差(好比進行1秒百萬級,或者只須要幾千個併發,在置null和GC以前程序就已經死掉,即垃圾回收不能根本解決程序內存消耗和性能問題,須要不產生垃圾或少產生垃圾),若是對程序性能和質量有更好的要求,結合兩種方式使用。
 
舉例:

1.public void Dispose()
   {
        GC.Collect();
        GC.SuppressFinalize(this);
   }

2.線程終止及清理

          _thread.Abort();
          _thread.DisableComObjectEagerCleanup();
          _thread = null;

3.更完全的垃圾回收

    /// <summary>
    ///設置線程工做的空間
    /// </summary>
    /// <param name="process">線程</param>
    /// <param name="minSize">最小空間</param>
    /// <param name="maxSize">最大空間</param>
    /// <returns></returns>
    [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
    public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
    /// <summary>      
    /// 釋放內存      
    /// </summary>      
    public static void ClearMemory()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
        }
    }

4.代碼重構太寬泛,自行總結

出處:https://blog.csdn.net/jiandanji123/article/details/79416398

==========================================================

C#如何當即回收內存

=

1.把對象賦值爲null

2.當即調用GC.Collect();
 
注意:這個也只是強制垃圾回收器去回收,但具體何時執行不肯定。 
 
代碼:
  public partial class Form1 : Form
    {
        [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet =System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
 
 
        public Form1()
        {
            InitializeComponent();
        }
 
 
        private void button1_Click(object sender, EventArgs e)
        {
            ClearMemory();
        }
 
 
        #region 內存回收
   public  void ClearMemory()
 {
     GC.Collect();
     GC.SuppressFinalize(this);
 
 
     if (Environment.OSVersion.Platform == PlatformID.Win32NT)
     {
         SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
     }
 }
 #endregion
 
 
   private void timer1_Tick(object sender, EventArgs e)
   {
       string s="clearMemory";
       Form dt = new Form();
       dt.Text = s;
       //若是垃圾產生於timer中可能沒法馬上回收資源,需加載timer tick事件中,馬上回收資源,或使用另外的timer控制回收時間。
       GC.Collect();
   }
 
}
[System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet =System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);

 #region 內存回收
   public  void ClearMemory()
 {
     GC.Collect();
     GC.SuppressFinalize(this);
 
 
     if (Environment.OSVersion.Platform == PlatformID.Win32NT)
     {
         SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
     }
 }
 #endregion

ClearMemory();

 

=

出處:https://blog.csdn.net/xwnxwn/article/details/78009071

http://www.javashuo.com/article/p-mrpvvqvo-dd.html

相關文章
相關標籤/搜索