又一个如何释放内存的问题:
我正在两个数据库之间复制数据,这两个数据库目前相同但很快就会失去同步。我使用Reflection和ADO.Net实体在C#中组建了一个骨架应用程序:
对于源数据库中的每个表:
这很好用,直到我到达用户上传文件的900MB大表。
将blob(每个最多7 MB)复制到我的计算机并返回到目标数据库的过程会耗尽本地内存。但是,该内存没有被释放,并且一旦复制了大约750 MB的数据,该进程就会消失 - 当抛出OutOfMemoryException时,我的程序具有1500 MB的已分配空间,大概是它到目前为止复制的所有内容的两个副本。
我先尝试了一种天真的方法,做了一个简单的复制。它一直在每张桌子上工作,直到我到达那个大桌子。我尝试强制GC.Collect()
,结果没有明显变化。我也尝试将实际副本放入一个单独的函数中,希望超出范围的引用可以帮助它获得GCed。我甚至放了一个Thread.Sleep
来尝试让后台进程有更多的时间来运行。所有这些都没有效果。
这是现在存在的相关代码:
public static void CopyFrom<TSource, TDest>(this ObjectSet<TDest> Dest, ObjectSet<TSource> Source, bool SaveChanges, ObjectContext context)
where TSource : class
where TDest : class {
int total = Source.Count();
int count = 0;
foreach (var src in Source) {
count++;
CopyObject(src, Dest);
if (SaveChanges && context != null) {
context.SaveChanges();
GC.Collect();
if (count % 100 == 0) {
Thread.Sleep(2000);
}
}
}
}
我没有包含CopyObject()函数,它只是使用反射来评估src的属性,并将它们放在一个新对象中的同名属性中,以附加到Dest。
SaveChanges是一个传递的布尔变量,表示应该完成这个额外的处理,它只在大表上才为真,否则为false。
所以,我的问题是:如何修改此代码以防止我内存不足?
答案 0 :(得分:4)
问题是您的数据库上下文在内部使用了大量缓存,并且它会保留大量信息并阻止垃圾收集器释放它(无论您是否调用Collect
)。
这意味着您的上下文定义为范围太高。 (看来,根据你的编辑,你在表格中使用它。这是......不好。)你没有显示它的定义,但无论它在哪里,它应该在较低的水平。请记住,由于连接池创建新的上下文并不昂贵,并且根据您的用例,您不需要依赖大量缓存的信息(因为您不会多次触摸项目),所以经常创建新环境不应该增加性能成本,即使它大大减少了你的内存占用。