在过去的几天里,我一直在尝试识别应用程序中的内存泄漏。我有充分的理由相信它来自这段代码;
List<Item> Items = new List<Item>();
List<Action> Actions = new List<Action>();
while (true)
{
//The queue is a singleton which is used by multiple threads.
//This method grabs the lock and dequeues 500 items at a time.
Items = ItemQueue.Instance.DequeuePackageByAmount(500);
if (Items == null) break;
for (int i = 0; i < Items.Count; i++)
{
int copy = i;
Actions.Add(new Action(() => DoSomethingWithItem(Items[copy])));
}
Parallel.Invoke(new ParallelOptions { MaxDegreeOfParallelism = 500 }, Actions.ToArray());
Items.Clear();
Actions.Clear();
}
Item
不包含任何应处理的未管理资源。为了完整起见;
public class Item
{
public ICollection<string> SomeCollection;
public string SomeString;
}
然而,当然;
public void DoSomethingWithItem(Item item)
{
ItemProcessor processor = new ItemProcessor();
processor.Process(item);
}
public class ItemProcessor
{
private DbContextWrapper db;
public ItemProcessor()
{
//DbContextWrapper contains methods which talk to the database.
//Every method has a using(), so there should be no DbContext resources
//left undisposed.
db = new DbContextWrapper();
}
public void Process(Item item)
{
foreach(string s in item.SomeCollection)
{
//check some things and push it to the next queue if valid
}
}
}
我知道我不应该问请找到内存泄漏,因为代码是伪的。因此;
这段代码是否可能容易受到内存泄漏的影响?如果是,那么在哪里?
编辑1:
进一步解释我有充分的理由相信它来自这段代码;我已经测试了几段代码,这件作品分配了最多的内存。大约5分钟后,我的应用程序使用大约1.6G RAM,不久之后,它崩溃了OutOfMemoryException
。
另外,为了进一步解释DbContextWrapper
,它看起来像这样;
public class DbContextWrapper
{
private string ConnectionString;
public DbContextWrapper()
{
//Create ConnectionString with EntityStringBuilder etc.
}
public int AnyMethod(int someExistingId)
{
using(DbContext db = new DbContext(ConnectionString))
{
return db.table.Where(t => t.id == someExistingId).First();
}
}
}
AFAIK,这样就不会有任何不受管理的非托管资源。
答案 0 :(得分:1)
如果您没有调试器/分析器等:
如果它是一个托管泄漏(不是非托管泄漏)你正在追捕你可以尝试(在我的记忆中):
运行应用程序直到发现内存泄漏。 转储(在异常发生之前!!) 在Windbg中打开并运行(假设为.NET 4):
.loadby sos clr
!dumpheap -stat
然后查看列表中的最后一个对象(包含大多数实例化的对象),并确定一个对于那么多实例化你不会期望的对象类型。 (你的一个或一个平行等)。
在该类型上运行:
!dumpheap -type [yourType]
注意:结果列出了所有以名称开头的类型,如示例System.Threading.Thread。 您将获得一个类似于(System.Threading.Thread示例)的列表:
...//single objects list
33767510 71d41144 56
42fb29e4 71d41144 56
42fbf2f0 71d41144 56
42fd6e18 71d41144 56
42fe62b0 71d41144 56
430715fc 71d41144 56
4344374c 71d41144 56
total 372 objects
Statistics: // summary of the classes listed in single object list
MT Count TotalSize Class Name
71d2ebd8 1 24 System.Threading.ThreadPoolRequestQueue
714c52b4 1 32 System.Threading.ThreadExceptionEventHandler
71d40f44 2 144 System.Threading.ThreadAbortException
71d285a4 41 820 System.Threading.ThreadHelper
71d27f48 82 2624 System.Threading.ThreadStart
>>71d41144 245 13720 System.Threading.Thread
第一部分是内存中所有对象的列表。列表中标记的类型是为示例选择的类型 对象列表中的第二个参数是类型的方法表。这是您可以在对象列表中识别不同类型的方法 获取对象列表的几个地址(例如42fd6e18或4344374c)并运行:
!gcroot <address>
对于几个地址。您将获得持有对可疑类型的引用的对象列表。你必须找到列表顶部的对象的原因,为什么它保持你的类型(实际上你认为它不应该)