我有一个在Visual Studio 2012中开发的.NET 4.0程序,它使用旧系统数据的数据集和新系统的实体框架将数据从遗留系统迁移到新系统。我一直在使用该程序存在严重的内存消耗/内存泄漏问题,特别是对于大型数据集,所以我使用windbg试图找到问题的根源。
程序的工作方式基本上是从多个遗留系统访问数据库中读取大量DataRows,并在List(of T)
中存储相同类型的行。接下来,各种过程遍历这些列表并创建保存到新系统中的新实体。在使用GetData()
的{{1}}方法将所有数据行读入给定列表后,我关闭了与Access数据库的连接。解析TableAdapter
中的所有项目后,清除列表,并将其设置为Nothing。为了通过EntityFramework保存到新数据库,我批量记录,以便我一次提交100条记录的更改,并在每次提交后处理,取消并重新安排EF List(of T)
。
再次执行ObjectContext
,GC.Collect()
和GC.WaitFullGCCollect()
后,我在调试时通过VisualStudio进行了内存转储,在解析并导入了所有记录之后,当进程向上显示时在任务管理器中消耗700mb内存,然后将该转储加载到windbg中。
当我发出GC.Collect()
命令时,我得到一长串对象及其实例数和内存消耗。值得注意的是,它位于此列表的底部,显示使用最多内存的对象。以下是该列表底部的片段:
!dumpheap -stat
正如你所看到的那样,仍然有一些TON的对象仍然存在于内存中,我希望它们不存在。特别是tblLogsDataRow对象和System.String对象。
这是我对enxt做什么的了解开始变得有点朦胧的地方。我在其中一些上执行了67ac8034 28 1233489 System.Boolean[]
025b0fd4 24829 1787688 Importer.dsIBET+tblTKNumberRow
67ad3a70 2840 16078424 System.Int32[]
67ac0f78 4 18874416 System.Double[]
67adfcd8 18 19136728 System.Decimal[]
67add67c 33 19657100 System.DateTime[]
101a04cc 508 25441232 System.Data.RBTree`1+Node[[System.Int32, mscorlib]][]
101a0b70 509 25442268 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
025b19b4 761228 54808416 Importer.dsIBET+tblLogsDataRow
005244a8 390 85776048 Free
67abfe8c 16421 90050676 System.Object[]
67ad224c 9211552 294563252 System.String
,例如system.string,然后随机选择一个超过900万个字符串实例中的一个并对其执行!gcroot,从中我可以看出字符串似乎与我的实体有关,在这一点上应该没有,因为在调用堆栈的这一点上,我已经处理掉了我的对象上下文。所以我不清楚为什么所有这些字符串都驻留在内存中,或者它们究竟在哪里。
就datarow对象而言,我也不了解这些是如何存在的,因为我将它们存储在一个列表中,然后清除该列表并使其无效。
显然,我没有做正确的事情,但我对如何解决这个问题感到有些迷茫。
所以我的问题是两部分:
1)您是否可以提供有关如何使用windbg获取任何可能有助于我诊断内存泄漏源的更多信息的指示?
2)就数据集和实体框架而言,你能否提供一些关于我做错的见解?即关于我将数据行存储在列表中然后清除这些列表的方式并没有释放这些列表所使用的内存,处理我的objectcontext似乎并没有释放它使用的任何资源。
谢谢, 约什
答案 0 :(得分:0)
使用任务管理器测量内存是一个错误。默认情况下,任务管理器显示专用工作集,它只是RAM中的内存。这不考虑已交换到磁盘的部分。任务管理器显示的内容在很大程度上取决于其他应用程任务管理器没有指示内存泄漏的列。
使用Process Explorer并让它显示"私有字节"柱。但无论如何,你很幸运,发现你的应用程序使用了太多内存。
您可能知道,.NET使用垃圾回收。当您释放对象时,它们不会立即收集,它们只适用于垃圾回收。这意味着:垃圾收集器需要运行才能收集它们。这可能会在以后的任何时间发生。
您可以尝试执行GC.Collect();
来强制进行垃圾回收。您的案例可能是GC.Collect()
有用且允许的很少情况之一。你似乎知道你已经分配了很多内存,你删除了引用,你想立即释放它,例如执行另一项任务。
但要注意其后果。请务必阅读并理解伟大的Proper use of IDisposable answer。
!gcroot
可能是正确的。字符串可能属于实体。别担心。垃圾收集器不仅可以收集单个对象,还可以释放浮动的对象岛。因此,一旦收集了实体对象,字符串也将消失。
更重要的问题是:实体对象是否已根植?如果是这样,他们就不会被垃圾收集。你可能已经处理了它们,但如果还有你忘记的参考,那么它们还活着。
目前您只检查Entity对象是否仍然是root(!gcroot
)。除此之外,我在WinDbg中看不到多少会有所帮助。