問題背景----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
==========================================================
=
1.把對象賦值爲null
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();
=