系统抛出了类型为“System.OutOfMemoryException”的异常。

9

我基本上使用Entity Framework来查询一个巨大的数据库。我想返回一个字符串列表,然后将其记录到文本文件中。

List<string> logFilePathFileName = new List<string>();
var query = from c in DBContext.MyTable where condition = something select c;
foreach (var result in query)
{
    filePath = result.FilePath;
    fileName = result.FileName;
    string temp = filePath + "." + fileName;
    logFilePathFileName.Add(temp);
    if(logFilePathFileName.Count %1000 ==0)
        Console.WriteLine(temp+"."+logFilePathFileName.Count);
}

然而,当 logFilePathFileName.Count=397000 时,我遇到了一个异常。
异常信息如下:

类型为'System.OutOfMemoryException'的异常被抛出。

'System.Data.Entity.dll'中发生了类型为'System.OutOfMemoryException'的一次性异常。

更新: 我想使用不同的查询,例如:选择前1000个,然后添加到列表中,但是我不知道超过1000之后该怎么办?

提示:升级你的内存条 :-) - Roy Dictus
你能提供关于异常的完整信息,包括内部异常和堆栈跟踪吗? - Hamlet Hakobyan
通常 temp 的长度是多少? - Cédric Bignon
内存为4GB,足够使用了。 - user1108948
2
https://dev59.com/JW865IYBdhLWcg3wQMSW - 001
OutOfMemoryException几乎从来都与物理内存无关。 - Alex
7个回答

15

很可能问题不是关于一个 RAM ,所以增加你的RAM ,甚至在64位机器上编译和运行你的代码也不会有积极的效果,在这种情况下。

我认为这与 .NET 集合被限制在最大 2GB 的 RAM 空间有关(无论是32位还是64 位)。

为了解决这个问题,将你的列表拆分成更小的块,很可能你的问题就解决了。

只是一种可能的解决方案:

foreach (var result in query)
{
    ....
    if(logFilePathFileName.Count %1000 ==0) {
        Console.WriteLine(temp+"."+logFilePathFileName.Count);
        //WRITE SOMEWHERE YOU NEED 
        logFilePathFileName = new List<string>(); //RESET LIST !|
    }
}

编辑

如果你需要对一个查询进行分段,可以使用Skip(...)Take(...)

这是一个解释性的例子:

var fisrt1000 = query.Skip(0).Take(1000);
var second1000 = query.Skip(1000).Take(1000);

自然地将其放入您的迭代中,并根据您知道或需要的数据边界进行参数化。


问题在于由于大量查询,数据库似乎也已经崩溃了。 - user1108948
你在服务器上遇到了什么异常? - Tigran
还没有,也许有其他的东西。请看我的更新,我想选择前1000个批量,然后迭代剩下的,怎么做? - user1108948
你可以使用 Skip(..)Take(..) 来分段或查询,或者在一个查询中获取所有内容但将列表分段。 - Tigran
我是指在1000之后,如何通过查询选择1001-2000?你不能仍然使用“select top 1000...”。 - user1108948

3
为什么你要将数据收集到一个List<string>中,如果你所需要做的就是写入文本文件?
你可以这样做:
- 打开文本文件; - 迭代记录,将每个字符串附加到文本文件中(不要在内存中存储字符串); - 刷新并关闭文本文件。
这样做可以节省大量的内存,因为你不必将所有这些字符串无谓地存储在内存中。

1
您可能需要设置一些vmargs以增加内存!另外...考虑直接写入文件而不是将其保存在列表中。

1
What Roy Dictus说的听起来是最好的方法。你也可以尝试给查询添加限制,这样你的数据库结果就不会太大。
关于以下信息: 使用实体框架限制查询大小

0

我曾经在VS C++中使用过类似于您使用的gc List的gc arraylist,它可以很好地处理小型和中等数据集,但是当处理大数据集时会出现“System.OutOfMemoryException”等问题。由于这些gcs的大小不能超过2 GB,因此在处理大数据时效率不高,因此我构建了自己的链表,它提供了相同的功能、动态增加和按索引获取数据,基本上它是一个正常的链表类,内部有一个动态数组来提供按索引获取数据的功能。它会复制空间,但是您可以在更新数组后删除链表,只保留动态数组,这将解决问题。请参见代码:

struct LinkedNode
{
    long data;
    LinkedNode* next;
};


class LinkedList
{
public:
    LinkedList();
    ~LinkedList();
    LinkedNode* head;
    long Count;
    long * Data;
    void add(long data);
    void update();
    //long get(long index);
};

LinkedList::LinkedList(){
    this->Count = 0;
    this->head = NULL;
}

LinkedList::~LinkedList(){
    LinkedNode * temp; 
    while(head){
        temp= this->head ;
        head = head->next;
        delete temp;
    }
    if (Data)
        delete [] Data; Data=NULL;
}

void LinkedList::add  (long data){
    LinkedNode * node = new LinkedNode();
    node->data = data;
    node->next = this->head;
    this->head = node;
    this->Count++;}

void LinkedList::update(){
    this->Data= new long[this->Count];
    long i = 0;
    LinkedNode * node =this->head;
    while(node){
        this->Data[i]=node->data;
        node = node->next;
        i++;
    }
}

如果您使用此内容,请参考我的工作https://www.liebertpub.com/doi/10.1089/big.2018.0064


0

你不应该将所有记录从数据库读取到列表中,这会占用大量内存。你可以将读取记录和写入文件结合起来。例如,从数据库中读取1000条记录到列表中并将它们保存(追加)到文本文件中,清除已使用的内存(list.Clear()),然后继续处理新的记录。


0

从StackOverflow上的其他话题中,我了解到Entity Framework并不适用于处理大量数据。EF会在上下文中缓存/跟踪所有数据,并在处理大量数据时引发异常。解决方案是直接使用SQL或将记录分成较小的集合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接