2010-09-06 68 views
11

我有一個存在大量內存泄漏的應用程序。例如,如果打開一個視圖並關閉10倍的內存消耗,則視圖不會完全清理。這些是我的內存泄漏。從測試驅動的角度來看,我想編寫一個測試來證明我的泄漏,並且(在我修復泄漏之後)聲稱我修復了它。這樣我的代碼將不會被打破。所以簡而言之:單元測試內存泄漏

有沒有辦法斷言我的代碼沒有從單元測試中泄漏內存?

例如我可以這樣做:

objectsThatShouldNotBeThereCount = MemAssertion.GetObjects<MyView>().Count; 
Assert.AreEqual(0, objectsThatShouldNotBeThereCount); 

我對分析不感興趣。我使用螞蟻探查器(我喜歡很多),但也想寫測試,以確保'泄漏'不會回來

我使用C#/ Nunit,但在任何有哲學的人...

回答

2

你不需要單元測試你需要內存分析器。您可以從CLR Profiler.

+4

我已經在使用一個探查器,但是想「扣住」我的結果,因此很容易爲同一場景創建一個新的泄漏 – Gluip 2010-09-06 14:11:19

4

內存消耗增加並不一定表示資源泄漏,因爲垃圾收集不確定且可能尚未啓動。即使你「放開」了對象,只要CLR認爲系統上有足夠的資源可用,CLR就可以隨意保留它們。

如果你知道你事實上有資源泄漏,你可以使用具有明確關閉/處置作爲他們的合同(意思是「使用...」結構)的一部分的對象。在這種情況下,如果您可以控制這些類型,那麼您可以在其Dispose實現中標記對象的處置,以驗證它們實際上是否已處置,如果您可以將生命週期管理置入該類型的接口中。

如果你選擇後者,可以進行合同處置發生的單元測試。我在某些場合已經這樣做了,使用與IDisposable相當的應用程序(擴展該接口),添加查詢對象是否已被處置的選項。如果你明確地在你的類型上實現了這個接口,它將不會污染它的接口。

如果您無法控制相關類型,則可以使用其他位置提供的內存分析器,它是您需要的工具。 (例如來自Jetbrains的dotTrace。)

+0

您的意思是我可以測試我的Dispose是否正在爲特定對象調用。這將是一個好的開始,儘管這將是一個非常具體的測試。 – Gluip 2010-09-06 14:14:31

+0

事實後很難進行合約處置的測試。這些測試屬於應用程序本身的單元測試。在事實之後,我用來查明違反契約處置的另一種方法是如果未設置IsDisposed標誌,則從類型的析構函數(警告應用!)中斷開Systems.Diagnostics.Assert失敗。這告訴你(在垃圾收集時)它發生了,但不是如何。但是,如果與對象的實例化時間保持一個StackTrace快照結合使用,您可以找到誰實例化它並回溯到爲什麼它不被處置。 – Cumbayah 2010-09-06 14:30:36

0

您可能會掛鉤到profiling API,但看起來您必須啓用您的單元測試並啓用Profiler。

如何創建對象?直接或某種可以控制的方式。如果可控制的返回擴展版本與終結器,它們已被註銷,它們已被處置。然後

GC.Collect(); 
GC.WaitForPendingFinalizers(); 
Assert.IsTrue(HasAllOfTypeXBeenFinalized()); 
+0

好主意。不幸的是我直接創建我的對象,所以我不能用額外的功能來包裝它們只是爲了測試。 – Gluip 2010-09-06 17:01:16

0

如何像:

long originalByteCount = GC.GetTotalMemory(true); 
SomeOperationThatMayLeakMemory(); 
long finalByteCount = GC.GetTotalMemory(true); 
Assert.AreEqual(originalByteCount, finalByteCount); 
+0

我剛試過,這不起作用。據我所知,nunit的痕跡增加了噪音,所以突然間應該是中性的手術不是。 – Johannes 2016-09-08 12:30:21

1

通常,當託管類型使用非託管資源不小心內存泄漏進行了介紹。

一個典型的例子是System.Threading.Timer,它將回調方法作爲參數。由於計時器最終使用非託管資源,所以引入了一個新的GC根,只能通過調用計時器的Dispose方法來釋放。在這種情況下,你的類型也應該實現IDisposable,否則這個對象永遠不會被垃圾收集(泄漏)。

你可以做類似的東西這個寫這種情況下一個單元測試:

 var instance = new MyType(); 

     // ... 
     // Use your instance in all the ways that may trigger creation of new GC roots 
     // ... 

     var weakRef = new WeakReference(instance); 

     instance.Dispose(); 
     instance = null; 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.Collect(); 
     Assert.IsFalse(weakRef.IsAlive); 
1

dotMemory Unit框架有能力以編程方式檢查分配的特定對象,內存業務量,使和比較內存快照。