1

另一个如何释放内存的问题:

我正在两个当前相同但很快就会不同步的数据库之间复制数据。我使用 Reflection 和 ADO.Net Entities 在 C# 中编写了一个框架应用程序,它执行以下操作:

对于源数据库中的每个表:

  • 清除目标数据库中的对应表
  • 对于源表中的每个对象
    • 对于源对象中的每个属性
      • 如果目标对象中存在同名属性,则使用反射将源属性复制到目标属性

在我找到包含用户上传文件的 900MB 大表之前,这非常有效。

将 blob(每个最多 7 MB)复制到我的机器并返回到目标数据库的过程会占用本地内存。但是,该内存没有被释放,一旦复制了大约 750 MB 的数据,该进程就会终止——当抛出 OutOfMemoryException 时,我的程序有 1500 MB 的分配空间,大概是到目前为止它复制的所有内容的两个副本。

我先尝试了一种天真的方法,做了一个简单的复制。它在每张桌子上都有效,直到我拿到大桌子为止。我曾尝试强制 aGC.Collect()对结果没有明显变化。我还尝试将实际副本放入一个单独的函数中,希望超出范围的引用有助于它获得 GC。我什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 是传入的布尔变量,表示应该进行此额外处理,仅在大表上为 true,否则为 false。

所以,我的问题是:如何修改此代码以不让我内存不足?

4

1 回答 1

4

问题是您的数据库上下文在内部使用了大量缓存,并且它保留了您的大量信息并阻止垃圾收集器释放它(无论您是否调用Collect)。

这意味着您的上下文定义的范围太高。(根据您的编辑,您似乎正在跨表使用它。那……不好。)您没有显示它的定义位置,但无论它在哪里,它可能应该处于较低级别。请记住,由于连接池创建新上下文并不昂贵,并且根据您的用例,您不需要依赖大量缓存信息(因为您不会多次触摸项目)如此频繁地创建新的上下文不应该增加性能成本,即使它大大减少了你的内存占用。

于 2012-11-08T15:56:42.710 回答