在一段代码中将GC置于保持状态

时间:2016-05-16 20:31:59

标签: c# .net garbage-collection .net-4.6

有没有办法让GC完全保留一段代码? 我在其他类似问题中发现的唯一问题是GC.TryStartNoGCRegion,但它仅限于您指定的内存量,它本身仅限于短暂段的大小。

有没有办法完全绕过它并告诉.NET "分配你需要的任何东西,不做GC期间" 或增加段的大小?根据我的发现,在许多核心服务器上最多只有1GB,这比我需要分配的还要少,但我不希望GC发生(我有多达数TB的可用内存,而且有数千个在该部分GC出现峰值时,我非常乐意将这些内容换成RAM使用量的10倍甚至100倍。

编辑:

现在有了赏金,我认为如果我指定用例会更容易。我正在使用LINQ to XML加载和解析一个非常大的XML文件(现在1GB,很快就会12GB)到内存中的对象。我不是在寻找替代方案。我从数百万XElements创建了数以百万计的小对象,并且GC正在尝试不间断地收集,同时我非常乐意保留所有RAM用完。我有100英镑的内存,一旦它达到4GB使用,GC开始收集不间断,这是非常友好的内存,但性能不友好。我不关心记忆,但我确实关心表现。我想采取相反的权衡。

虽然我无法在这里发布实际代码,但是一些示例代码非常接近最终代码,可以帮助那些要求更多信息的人:

var items = XElement.Load("myfile.xml")
.Element("a")
.Elements("b") // There are about 2 to 5 million instances of "b"
.Select(pt => new
{
    aa = pt.Element("aa"),
    ab = pt.Element("ab"),
    ac = pt.Element("ac"),
    ad = pt.Element("ad"),
    ae = pt.Element("ae")
})
.Select(pt => new 
{
    aa = new
    {
        aaa = double.Parse(pt.aa.Attribute("aaa").Value),
        aab = double.Parse(pt.aa.Attribute("aab").Value),
        aac = double.Parse(pt.aa.Attribute("aac").Value),
        aad = double.Parse(pt.aa.Attribute("aad").Value),
        aae = double.Parse(pt.aa.Attribute("aae").Value)
    },
    ab = new
    {
        aba = double.Parse(pt.aa.Attribute("aba").Value),
        abb = double.Parse(pt.aa.Attribute("abb").Value),
        abc = double.Parse(pt.aa.Attribute("abc").Value),
        abd = double.Parse(pt.aa.Attribute("abd").Value),
        abe = double.Parse(pt.aa.Attribute("abe").Value)
    },
    ac = new
    {
        aca = double.Parse(pt.aa.Attribute("aca").Value),
        acb = double.Parse(pt.aa.Attribute("acb").Value),
        acc = double.Parse(pt.aa.Attribute("acc").Value),
        acd = double.Parse(pt.aa.Attribute("acd").Value),
        ace = double.Parse(pt.aa.Attribute("ace").Value),
        acf = double.Parse(pt.aa.Attribute("acf").Value),
        acg = double.Parse(pt.aa.Attribute("acg").Value),
        ach = double.Parse(pt.aa.Attribute("ach").Value)
    },
    ad1 = int.Parse(pt.ad.Attribute("ad1").Value),
    ad2 = int.Parse(pt.ad.Attribute("ad2").Value),
    ae = new double[]
    {
        double.Parse(pt.ae.Attribute("ae1").Value),
        double.Parse(pt.ae.Attribute("ae2").Value),
        double.Parse(pt.ae.Attribute("ae3").Value),
        double.Parse(pt.ae.Attribute("ae4").Value),
        double.Parse(pt.ae.Attribute("ae5").Value),
        double.Parse(pt.ae.Attribute("ae6").Value),
        double.Parse(pt.ae.Attribute("ae7").Value),
        double.Parse(pt.ae.Attribute("ae8").Value),
        double.Parse(pt.ae.Attribute("ae9").Value),
        double.Parse(pt.ae.Attribute("ae10").Value),
        double.Parse(pt.ae.Attribute("ae11").Value),
        double.Parse(pt.ae.Attribute("ae12").Value),
        double.Parse(pt.ae.Attribute("ae13").Value),
        double.Parse(pt.ae.Attribute("ae14").Value),
        double.Parse(pt.ae.Attribute("ae15").Value),
        double.Parse(pt.ae.Attribute("ae16").Value),
        double.Parse(pt.ae.Attribute("ae17").Value),
        double.Parse(pt.ae.Attribute("ae18").Value),
        double.Parse(pt.ae.Attribute("ae19").Value)
    }
})
.ToArray();

3 个答案:

答案 0 :(得分:3)

我认为在你的情况下最好的解决方案是我之前在我的一个项目中使用的这段代码

     func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    var cell = myTable.dequeueReusableCellWithIdentifier("addFollowCell") as! AddFollowTableViewCell
    if(cell.identifier == true){
        cell.answerText.text = selectedAnswerForRow[indexPath.row]
    }
    cell.questionView.text = listQuestion1[indexPath.row]
    cell.pickerDataSource = dictPicker[indexPath.row]!
    dictAnswer[indexPath.row] = cell.pickerValue
    cell.answerText.addTarget(self, action: #selector(AddFollowUpViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingDidEnd)
    cell.identifier = true

  return cell
}

func textFieldDidChange(sender: UITextField){
    let rowIndex: Int!
    let cell = sender.superview?.superview as! AddFollowTableViewCell
    rowIndex = myTable.indexPathForCell(cell)?.row
    selectedAnswerForRow[rowIndex] = cell.answerValue
    print(selectedAnswerForRow[rowIndex])
    cell.answerText.text = sender.text
    cell.identifier = true

}

你尽可能地压抑(根据我的知识),你仍然可以手动拨打var currentLatencySettings = GCSettings.LatencyMode; GCSettings.LatencyMode = GCLatencyMode.LowLatency; //your operations GCSettings.LatencyMode = currentLatencySettings;

查看MSDN文章here

此外,我强烈建议使用LINQ GC.Collect()Skip()方法分析已解析的集合。最后加入输出数组

答案 1 :(得分:2)

目前我能找到的最好的是切换到具有更大段大小的服务器GC(它本身没有任何改变),让我使用更大的数字来表示没有gc部分:

        GC.TryStartNoGCRegion(10000000000); // On Workstation GC this crashed with a much lower number, on server GC this works

这违背了我的期望(这是10GB,但是从我在网上的文档中可以找到的,我当前设置中的段大小应该是1到4GB,所以我期望一个无效的参数)。

通过这个设置,我有我想要的东西(GC处于暂停状态,我分配了22GB而不是7,所有临时对象都没有GC,但GC运行一次(一次!)整个批处理过程而不是每秒许多次(在改变视觉工作室中的GC视图之前看起来像GC触发的所有单个点的直线)。

这不是很好,因为它不会缩放(添加0会导致崩溃),但它比我到目前为止发现的任何其他东西都要好。

除非有人发现如何增加分段大小以便我可以进一步推动这个或者有一个更好的替代方案来完全停止GC(而不仅仅是某一代但是所有这一代)我将接受我自己的答案几天。

答案 2 :(得分:1)

我不确定您的情况是否可行,但是您是否尝试过并行处理XML文件。如果您可以在较小的部分中分解XML文件,则可以在代码中生成多个进程。每个进程处理一个单独的文件。然后,您可以组合所有结果。这肯定会提高您的性能,并且对于每个进程,您将单独分配内存,这也应该在处理所有XML文件时增加特定时间的内存分配。