我正在尝试构建一些对象并将它们插入到数据库中。必须插入的记录数量大〜数百万。 插入是分批完成的。 我遇到的问题是我需要初始化新对象以将它们添加到列表中,最后,我将批量插入到列表的数据库中。因为我正在初始化大量的对象,我的计算机内存(RAM)被填满了,它有点冻结了一切。 问题是 : 从内存的角度来看,我应该初始化将它们设置为null的对象吗? 此外,我正在尝试使用相同的对象引用。我做得对吗?
代码:
QACompleted completed = new QACompleted();
QAUncompleted uncompleted = new QAUncompleted();
QAText replaced = new QAText();
foreach (QAText question in questions)
{
MatchCollection matchesQ = rgx.Matches(question.Question);
MatchCollection matchesA = rgx.Matches(question.Answer);
foreach (GetKeyValues_Result item in values)
{
hasNull = false;
replaced = new QAText(); <- this object
if (matchesQ.Count > 0)
{
SetQuestion(matchesQ, replaced, question, item);
}
else
{
replaced.Question = question.Question;
}
if (matchesA.Count > 0)
{
SetAnswer(matchesA,replaced,question,item);
}
else
{
replaced.Answer = question.Answer;
}
if (!hasNull)
{
if (matchesA.Count == 0 && matchesQ.Count == 0)
{
completed = new QACompleted(); <- this object
MapEmpty(replaced,completed, question.Id);
}
else
{
completed = new QACompleted(); <- this object
MapCompleted(replaced, completed, question.Id, item);
}
goodResults.Add(completed);
}
else
{
uncompleted = new QAUncompleted(); <- this object
MapUncompleted(replaced,uncompleted,item, question.Id);
badResults.Add(uncompleted);
}
}
var success = InsertIntoDataBase(goodResults, "QACompleted");
var success1 = InsertIntoDataBase(badResults, "QAUncompleted");
}
我已标记了这些对象。我应该像被替换= NULL一样调用它们,还是应该使用构造函数? 新的QAText()和= null之间有什么区别?
答案 0 :(得分:0)
实例化新(尽管是空的)对象总是占用一些内存,因为它必须为对象的字段分配空间。如果您不打算在实例中访问或设置任何数据,我认为没有必要创建它。
答案 1 :(得分:0)
不幸的是,代码示例写得不好。似乎遗漏了许多声明,并且代码中没有文档的副作用。这使得提供具体建议变得非常困难。
那说......
您的replaced
对象似乎不会在循环的一次迭代之后保留,因此它不是问题的一部分。 completed
和uncompleted
对象被添加到列表中,因此它们会增加内存消耗。同样地,goodResults
和badResults
列出了自己(这些声明在哪里?)。
如果您使用的RAM太少,那么是的......您将遇到性能问题,因为Windows使用磁盘来弥补RAM的不足。即使有足够的RAM,在某些时候你可能会遇到.NET对象大小的限制(即你只能将如此多的元素放入列表中)。因此,您似乎需要减少峰值内存使用量。
您声明当列表中的数据插入数据库时,列表会被清除。所以可能这意味着values
列表中有很多元素(代码示例中未声明的,未记录的变量之一),列表及其对象在到达内部循环结束之前变得太大了将数据插入数据库。
在这种情况下,解决问题的最简单方法似乎是在内部foreach
循环中内部批量提交更新。例如。在该循环结束时,添加如下内容:
if (goodResults.Count >= 100000)
{
var success = InsertIntoDataBase(goodResults, "QACompleted");
}
if (badResults.Count >= 100000)
{
var success = InsertIntoDataBase(badResults, "QACompleted");
}
(当然,将实际截止值声明为命名常量,并根据需要处理数据库插入结果返回值。)
当然,你仍然会在外循环的末尾进行插入。
答案 2 :(得分:0)
创建对象的内存成本
在C#中创建对象总是会有内存成本。这涉及对象的存储器布局。假设您使用的是64位操作系统,运行时必须为同步块分配额外的8个字节,为方法表指针分配8个字节。同步块和方法表指针是您的自定义数据字段后。除了不可避免的16字节头之外,对象总是与8字节的边界对齐,因此可能产生额外的开销。
如果您确切知道您创建的对象数量,则可以粗略估计内存开销。但是我建议你在假设你的内存压力来自对象布局开销时要小心。这也是我建议你估算开销的第一步的原因。您可能最终意识到即使布局开销可以神奇地完全删除,您也不会在内存性能方面产生巨大差异。毕竟,对于一百万个对象,对象头的开销只有16 MB。
被替换=新QAText()和被替换= null
之间的区别我想在你设置被替换为null后你仍然需要创建另一个QAText()?如果是这样,那么在内存方面,垃圾收集器没有真正的区别。如果您没有对其进行任何其他引用,则将以任一方式收集旧的QAText实例。但是,何时收集实例是垃圾收集器的调用。执行被替换= null不会使GC更早发生。
您可以尝试重复使用相同的QAText实例,而不是每次都创建一个新实例。但每次创建一个新的不会导致高内存压力。它会使GC更加繁忙,从而导致更高的CPU使用率。
确定高内存使用率的真正原因
如果您的应用程序确实使用了大量内存,则必须查看QACompleted和QAUncompleted对象的设计。这些是添加到列表中的对象并占用内存,直到您将它们提交到数据库。如果这些对象设计得很好(它们只占用了他们必须占用的内存),正如彼得所指出的那样,你应该使用较小的批量大小,这样你就不必将太多的对象留在内存中。
您的程序中还有其他因素可能导致意外的内存使用。 goodResults和badResults的数据结构是什么?它们是List还是LinkedList?内部列表只是一个动态数组。它使用了一个增长策略,当它满时,它的大小总是加倍。 always-double策略可以快速占用内存,特别是当你有很多条目时。
另一方面,LinkedList没有遭受上述问题。但是每个节点大约需要40个字节。还值得检查MapCompleted和MapUnCompleted方法正在做什么。他们是否长期参考replaced
对象?如果是这样,它将导致内存泄漏。
总而言之,在处理内存问题时,您应该关注宏范围问题,例如数据结构的选择或内存泄漏。或者优化您的算法,这样您就不必一直将所有数据保存在内存中。