DbContext OutOfMemoryException

本文关键字:OutOfMemoryException DbContext | 更新日期: 2023-09-27 18:09:12

我有一个DbContext>20M记录的数据集,必须转换为不同的数据格式。因此,我将数据读入内存,执行一些任务,然后处理DbContext。代码工作得很好,但过了一会儿,我得到了OutOfMemoryExceptions。我已经能够将其缩小到以下代码段,在这里我检索2M条记录,然后释放它们并再次获取它们。第一次检索工作正常,第二次则抛出异常。

// first call runs fine
using (var dbContext = new CustomDbContext())
{
    var list = dbContext.Items.Take(2000000).ToArray();
    foreach (var item in list)
    {
        // perform conversion tasks...
        item.Converted = true;
    }
}
// second call throws exception
using (var dbContext = new CustomDbContext())
{
    var list = dbContext.Items.Take(2000000).ToArray();
    foreach (var item in list)
    {
        // perform conversion tasks...
        item.Converted = true;
    }
}

GC不应该自动释放在第一个using块中分配的所有内存,这样第二个块应该像第一个块一样运行良好吗?

在我的实际代码中,我没有一次检索200万条记录,而是在每次迭代中检索0到30K之间的记录。然而,大约15分钟后,我耗尽了内存,尽管所有的对象都应该被释放。

DbContext OutOfMemoryException

我猜你见过LOH了。可能你的对象比阈值大,而且它们正在达到阈值,因此GC在默认情况下不起作用。

试试:https://www.simple-talk.com/dotnet/.net-framework/large-object-heap-compaction-should-you-use-it/

,看看你的异常是否消失。

。在第一部分和第二部分之间添加:

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(); 

IEnumerable有GetEnumerator(),所以你可以尝试这样做,以避免不必要的。toarray()或。tolist(),如果你只是想读取:

// first call
using (var dbContext = new CustomDbContext())
{
    foreach (var item in dbContext.Items.Take(2000000))
    {
        // perform conversion tasks...
        item.Converted = true;
    }
}
// second call
using (var dbContext = new CustomDbContext())
{
    foreach (var item in dbContext.Items.Take(2000000))
    {
        // perform conversion tasks...
        item.Converted = true;
    }
}

运行GC不会帮助您,您必须在不同的上下文中运行每个迭代。并处理您的上下文。

// ID is your primary key
long startID = 0;
while(true){
    using(var db = new CustomDbContext()){
        var slice = db.Items.Where(x=>x.ID > startID)
                            .OrderBy(x=>x.ID)
                            .Take(1000).ToList();
        // stop if there is nothing to process
        if(!slice.Any())
             break;
        foreach(var item in slice){
            // your logic...
            item.Converted = true;
        }
        startID = slice.Last().ID;
    }
}

如果您想更快地处理这些事情,另一种方法是并行运行片....

替代方法

我建议使用100x100分割切片,这样我就可以并行处理100个条目的100个切片。

你总是可以很容易地自定义切片,以满足您的速度需求。

public IEnumerable<IEnumerable<T>> Slice(IEnumerable<T> src, int size){
     while(src.Any()){
         var s = src.Take(size);
         src = src.Skip(size);
         yield return s;
     }
}
long startID = 0;
while(true){
    using(var db = new CustomDbContext()){
        var src = db.Items.Where(x=>x.ID > startID)
                            .OrderBy(x=>x.ID)
                            .Take(10000).Select(x=>x.ID).ToList();
        // stop if there is nothing to process
        if(!src.Any())
             break;
        Parallel.ForEach(src.Slice(100), slice => {
             using(var sdb = new CustomDbContext()){
                 foreach(var item in sdb.Items.Where(x=> slice.Contains(x.ID)){
                     item.Converted = true;
                 }
             }
        } );
        startID = src.Last();
    }
}

重构后,内存被释放。我不知道为什么,但它有效。

private static void Debug()
{
    var iteration = 0;
    while(true)
    {
        Console.WriteLine("Iteration {0}", iteration++);
        Convert();
    }
}
private static void Convert()
{
    using (var dbContext = new CustomDbContext(args[0]))
    {
        var list = dbContext.Items.Take(2000000).ToList();
        foreach (var item in list)
        {
            item.Converted = true;
        }
    }
}

当我将Convert()的内容移动到Debug()中的while循环时,抛出OutOfMemoryExceptions。

private static void Debug()
{
    var iteration = 0;
    while(true)
    {
        Console.WriteLine("Iteration {0}", iteration++);
        using (var dbContext = new CustomDbContext(args[0]))
        {
            // OutOfMemoryException in second iteration
            var list = dbContext.Items.Take(2000000).ToList(); 
            foreach (var item in list)
            {
                item.Converted = true;
            }
        }
    }
}
相关文章:
  • 没有找到相关文章