在一段代码中暂停 GC
Put GC on hold during a section of code
有没有办法让一段代码完全搁置GC?
我在其他类似问题中发现的唯一一件事是 GC.TryStartNoGCRegion
但它仅限于您指定的内存量,它本身仅限于临时段的大小。
有没有办法完全绕过它并告诉 .NET "allocate whatever you need, don't do GC period" 或增加段的大小?据我发现,它在多核服务器上最多为 1GB,这比我需要分配的要少,但我不希望 GC 发生(我有多达 TB 的可用 RAM,并且有数千个 GC 尖峰在那个部分,我非常乐意用 RAM 使用量的 10 倍甚至 100 倍进行交易。
编辑:
既然有赏金,我认为如果指定用例会更容易。我正在使用 LINQ to XML 将一个非常大的 XML 文件(目前为 1GB,很快将达到 12GB)加载并解析到内存中的对象中。我不是在寻找替代方案。我正在从数百万个 XElements
创建数百万个小对象,并且 GC 正试图不停地收集,而我很乐意让所有 RAM 用完。我有 100 GB 的 RAM,一旦使用了 4GB,GC 就会开始不停地收集,这对内存非常友好,但对性能不友好。我不关心内存,但我关心性能。我想采取相反的取舍。
虽然我不能 post 这里的实际代码是一些非常接近最终代码的示例代码,可以帮助那些需要更多信息的人:
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();
我认为对你来说最好的解决方案是我以前在我的一个项目中使用的这段代码
var currentLatencySettings = GCSettings.LatencyMode;
GCSettings.LatencyMode = GCLatencyMode.LowLatency;
//your operations
GCSettings.LatencyMode = currentLatencySettings;
你已经尽力压制了(据我所知),你仍然可以手动调用 GC.Collect()
。
看MSDN文章here
此外,我强烈建议使用 LINQ Skip()
和 Take()
方法对已解析的集合进行分页。最后加入输出数组
目前我能找到的最好的方法是切换到具有更大段大小的服务器 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 运行一次(一次!)批处理而不是每秒多次(在更改之前 visual studio 中的 GC 视图看起来像一条直线,来自所有单独的 GC 触发点)。
这不是很好,因为它不会缩放(添加 0 会导致崩溃)但它比我目前发现的任何其他东西都要好。
除非有人发现如何增加段大小以便我可以进一步推进或有更好的选择来完全停止 GC(不仅是某一代而是所有代)我会接受我自己的答案几天后。
我不确定您的情况是否可行,但是您是否尝试过并行处理 XML 文件。如果可以将 XML 文件分解成更小的部分,则可以从代码中生成多个进程。每个进程处理一个单独的文件。然后您可以合并所有结果。这肯定会提高您的性能,并且对于每个进程,您将单独分配内存,这也应该在处理所有 XML 文件时增加特定时间的内存分配。
有没有办法让一段代码完全搁置GC?
我在其他类似问题中发现的唯一一件事是 GC.TryStartNoGCRegion
但它仅限于您指定的内存量,它本身仅限于临时段的大小。
有没有办法完全绕过它并告诉 .NET "allocate whatever you need, don't do GC period" 或增加段的大小?据我发现,它在多核服务器上最多为 1GB,这比我需要分配的要少,但我不希望 GC 发生(我有多达 TB 的可用 RAM,并且有数千个 GC 尖峰在那个部分,我非常乐意用 RAM 使用量的 10 倍甚至 100 倍进行交易。
编辑:
既然有赏金,我认为如果指定用例会更容易。我正在使用 LINQ to XML 将一个非常大的 XML 文件(目前为 1GB,很快将达到 12GB)加载并解析到内存中的对象中。我不是在寻找替代方案。我正在从数百万个 XElements
创建数百万个小对象,并且 GC 正试图不停地收集,而我很乐意让所有 RAM 用完。我有 100 GB 的 RAM,一旦使用了 4GB,GC 就会开始不停地收集,这对内存非常友好,但对性能不友好。我不关心内存,但我关心性能。我想采取相反的取舍。
虽然我不能 post 这里的实际代码是一些非常接近最终代码的示例代码,可以帮助那些需要更多信息的人:
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();
我认为对你来说最好的解决方案是我以前在我的一个项目中使用的这段代码
var currentLatencySettings = GCSettings.LatencyMode;
GCSettings.LatencyMode = GCLatencyMode.LowLatency;
//your operations
GCSettings.LatencyMode = currentLatencySettings;
你已经尽力压制了(据我所知),你仍然可以手动调用 GC.Collect()
。
看MSDN文章here
此外,我强烈建议使用 LINQ Skip()
和 Take()
方法对已解析的集合进行分页。最后加入输出数组
目前我能找到的最好的方法是切换到具有更大段大小的服务器 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 运行一次(一次!)批处理而不是每秒多次(在更改之前 visual studio 中的 GC 视图看起来像一条直线,来自所有单独的 GC 触发点)。
这不是很好,因为它不会缩放(添加 0 会导致崩溃)但它比我目前发现的任何其他东西都要好。
除非有人发现如何增加段大小以便我可以进一步推进或有更好的选择来完全停止 GC(不仅是某一代而是所有代)我会接受我自己的答案几天后。
我不确定您的情况是否可行,但是您是否尝试过并行处理 XML 文件。如果可以将 XML 文件分解成更小的部分,则可以从代码中生成多个进程。每个进程处理一个单独的文件。然后您可以合并所有结果。这肯定会提高您的性能,并且对于每个进程,您将单独分配内存,这也应该在处理所有 XML 文件时增加特定时间的内存分配。