寫了一個window服務,循環更新sqlite記錄,內存一點點穩步增加。三天後,內存溢出。因而,我從本身的代碼入手,查找到底哪兒佔用內存釋放不掉,最終明確是調用servicestack.ormlite更新sqlite數據庫形成的。至因而不是框架問題,可能性不大,由於本地模擬執行的代碼沒有任何問題。我以爲應該是orm在執行數據庫更新後,對象還在被引用形成的。這裏,我貼出一個僞代碼:sql
//存放對象的一個列表 static List<Record> data=new List<Record>(5000); while(true){ var models = ReadDB(5000); data.AddRange(models); //更新model對象的字段 Dbhelp.UpdateAll(models);
data.Clear(); }
個人猜想到底對不對呢?如今還不知道。不過在探尋答案的時候,對GC的相關機制詳細地瞭解了一遍。數據庫
1、什麼是GC?c#
官網中有這麼一句話:api
The garbage collector is a common language runtime component that controls the allocation and release of managed memory。網絡
原來GC是CLR的一個組件,它控制內存的分配與釋放。app
2、託管堆和CLR堆管理器框架
咱們知道c#中的引用類型,分配在堆上。所謂的堆,就是一大塊連續的內存地址。CLR堆管理器負責內存的分配、釋放。堆又分爲小對象堆和大對象堆。它的內存分配流程以下:ide
圖片來源《.NET高級調試》pdfthis
CLR加載時,就會分配堆。spa
3、GC的工做機制
GC有三個假設:
一、若是沒有特別聲明,全部的對象都是垃圾(經過引用追蹤對象是否爲垃圾)
二、假設託管堆上全部的對象的活躍時間都是短暫的(相對於長久活躍的對象來講,GC將更頻繁地收集短暫活躍的對象)
三、經過代跟蹤對象的持續時間
如下是官方文檔給出的和這三個假設一致
The garbage collector in the common language runtime supports object aging using generations
Objects created more recently are part of newer generations, and have lower generation numbers than objects created earlier in the application life cycle.
Objects in the most recent generation are in generation 0. This implementation of the garbage collector supports three generations of objects, generations 0, 1, and 2
每代都有本身的堆,假如0代的堆滿了,就會觸發GC,而後把依然有引用的對象升級,放到1代對象。最後壓縮堆,把剩餘的堆空間合併到一塊。1代對象也是如此操做。但到了2代,就處理不一樣了。2代的堆多是大對象堆,它的壓縮代價過於高昂,因此只是合併相鄰的空間。
圖片來源博客園c#技術漫談之垃圾回收(GC)
Garbage collection happens automatically when a request for memory cannot be satisfied using available free memory
GC發生的時機,就是相應的堆達到了閾值,由於堆也有大小限制,並非無限的。儘管2代堆或者大對象堆滿的時候,經過增長新的內存段來知足內存分配,若是沒有可用的內存,這時就會報內存溢出。
4、GC不能釋放非託管資源
有兩種狀況,第一種:託管代碼引用了非託管資源,好比文件操做、數據庫鏈接、網絡鏈接等。這時候必須手動釋放,或實現 dispose模式,或實現對象終結 。第二種:非託管代碼使用了託管代碼。這種狀況,GC是能夠回收託管對象的,由於它檢測不到非託管代碼的引用。
When a type uses unmanaged resources that must be released before instances of the type are reclaimed, the type can implement a finalizer.
In most cases, finalizers are implemented by overriding the Object.Finalize method; however, types written in C# or C++ implement destructors, which compilers turn into an override of Object.Finalize
必須注意的一點是,實現對象終結器,GC會在釋放對象以前自動調用。其實這是一個代價很是高昂的備用機制。因此能本身釋放非託管資源的,就本身釋放。
若是一個對象中包含有終結器,那麼在new的時候放入到終結者隊列。當GC會把這個對象標爲垃圾時,放入到另外一個隊列F-Reachable中。這個隊列包含了全部帶有終結器而且將被做爲垃圾收集的對象,這些對象的終結器都將被執行。在垃圾收集的過程總並不會執行終結器代碼。而是由.NET 進程的終結線程調用。所以,此時的垃圾回收滯後一段時間,目的在於等待終結器代碼執行的完成。
5、dispose模式
1 using System; 2 3 class BaseClass : IDisposable 4 { 5 // Flag: Has Dispose already been called? 6 bool disposed = false; 7 8 // Public implementation of Dispose pattern callable by consumers. 9 public void Dispose() 10 { 11 Dispose(true); 12 GC.SuppressFinalize(this); 13 } 14 15 // Protected implementation of Dispose pattern. 16 protected virtual void Dispose(bool disposing) 17 { 18 if (disposed) 19 return; 20 21 if (disposing) { 22 // Free any other managed objects here. 23 // 24 } 25 26 // Free any unmanaged objects here. 27 // 28 disposed = true; 29 } 30 31 ~BaseClass() 32 { 33 Dispose(false); 34 } 35 }
1 using Microsoft.Win32.SafeHandles; 2 using System; 3 using System.Runtime.InteropServices; 4 5 class DerivedClass : BaseClass 6 { 7 // Flag: Has Dispose already been called? 8 bool disposed = false; 9 // Instantiate a SafeHandle instance. 10 SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true); 11 12 // Protected implementation of Dispose pattern. 13 protected override void Dispose(bool disposing) 14 { 15 if (disposed) 16 return; 17 18 if (disposing) { 19 handle.Dispose(); 20 // Free any other managed objects here. 21 // 22 } 23 24 // Free any unmanaged objects here. 25 // 26 27 disposed = true; 28 // Call base class implementation. 29 base.Dispose(disposing); 30 } 31 }
這是基類和子類的dispose模式,來源於官網。