Int / Int64 .Net内存分配

3
我有一个大型应用程序,平均每秒内存分配约为30 MB/秒(根据性能监视器字节分配/秒测量)。我正在努力将其大幅缩减,但分配的来源并不明显。
为了检测问题,我录制了CLR/GC的ETW跟踪,并导出了AllocationTick事件,该事件记录了每次分配了额外的100 KB以及最近分配的对象类型。这产生了一个不错的样本集。三种对象类型占了我分配的70%,但它们还有点神秘。
1. System.Int64 30% 2. System.Int32 28% 3. System.Runtime.CompilerServices.CallSite'1[System.Func'3[System.Runtime.CompilerServices.CallSite,System.Object,System.Object]] 12%
数据集为约70分钟和一百万个事件,因此我对这些数字非常有信心。
我猜测这可能表明我以某种意外的方式在堆上创建了大量指针?(这是一个x64应用程序)
我使用了一些LINQ和foreach循环,但这些只应在堆栈上创建增量变量,而不是堆上。
我还在使用TPL/Dataflow库,这可能会产生这些问题。
我正在寻找有关可能导致如此多的int32/64堆分配的任何建议,并且可能有一些隔离这些分配的技术(调用堆栈非常好,但可能会影响性能)。
1个回答

5
我猜测这是在某些意外的情况下在堆上创建许多指针?
对我来说,更有可能是装箱了大量int和long值。
"CallSite"部分听起来像你经常使用"dynamic"(或代码中一个非常频繁使用的部分),这很容易导致比静态类型代码更多的装箱。
我会尝试隔离分配大量对象的特定代码区域。例如,如果您可以仅运行特定代码路径,那么这将为您提供更清晰的思路,了解哪些路径生成的垃圾比您预期的要多得多。查看任何使用"dynamic"的地方,并检查您是否真的需要它。虽然您不应该觉得必须删除所有"dynamic"的用法,但可能会有一个特定的“热点”可以进行微观优化。
另一件要考虑的事情是这种分配实际上会给您带来多少成本。您说正在努力大幅减少它-您真的需要吗?如果所有这些对象都很短暂,您可能会发现通过降低分配速率并不能显著改善性能。您应该测量垃圾回收所花费的时间,以确定这可能会有多大的影响。

动态洞察是一个巨大的提示。我只在一些地方用动态调试,而且在同样的调试代码中我正在将结果装箱。谢谢! - Superman
另外一件事 - 非常有帮助,回复也非常快。我不知道你是如何保持这么敏锐的反应速度的(现在加州已经是午夜了)。虽然分配的生命周期很短,但由于Gen-0收集引起的多次暂停,它们正在引发我们应用程序中的一些尾部事件。 - Superman
@超人:在工作日,我通常在欧洲/伦敦时间午夜到早上6点左右睡觉,在周末则是午夜到早上7点。看起来你处于一个很理想的优化位置,只需要处理一些热点问题 - 这总是一个不错的位置。 - Jon Skeet

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