問題背景----
今天寫了個很小的程序,程序的功能僅僅是截圖,但是如果長時間開啟並截圖的時候,程序會變的很大,從剛開始的運行在任務管理器中只有十幾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,說明符合官方解釋;
根據以上代碼測試知道,當循環5次的時候,GC並不會立即執行,所以當執行5次循環的時候第8行沒起作用。既然不起作用,我們把他注釋暫時不用,把11和12行開啟。
3)把地8行注釋,11,12行開啟,執行5次循環,發現15行輸出1,多次執行結果相同。
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
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();
=