C# 使用委托会产生垃圾吗

C# 使用委托会产生垃圾吗,c#,delegates,garbage-collection,xna,xbox360,C#,Delegates,Garbage Collection,Xna,Xbox360,我正在使用XNA为xbox360开发一款游戏。在Xbox上,垃圾收集器与PC上的垃圾收集器相比性能相当差,因此将产生的垃圾保持在最低限度对于游戏的顺利进行至关重要 我记得曾经读过一篇文章,说调用代理会产生垃圾,但现在我一辈子都找不到任何关于代理创建垃圾的引用。这是我编造的还是代表们乱七八糟 如果学员混乱不堪,建议解决方案可获得额外分数 public delegate T GetValue<T>(T value, T[] args); public static T Transact

我正在使用XNA为xbox360开发一款游戏。在Xbox上,垃圾收集器与PC上的垃圾收集器相比性能相当差,因此将产生的垃圾保持在最低限度对于游戏的顺利进行至关重要

我记得曾经读过一篇文章,说调用代理会产生垃圾,但现在我一辈子都找不到任何关于代理创建垃圾的引用。这是我编造的还是代表们乱七八糟

如果学员混乱不堪,建议解决方案可获得额外分数

public delegate T GetValue<T>(T value, T[] args);

public static T Transaction<T>(GetValue<T> calculate, ref T value, params T[] args) where T : class
{
    T newValue = calculate(value, args);
    return foo(newValue);
}
public委托T GetValue(T value,T[]args);
公共静态T事务(GetValue calculate,ref T value,params T[]args),其中T:class
{
T newValue=计算(值,args);
返回foo(newValue);
}

我的代码目前看起来很模糊,我能想到的摆脱委托的唯一解决方案是传入一个继承了接口IValueCalculator的类,然后我可以在该接口上调用该方法,但这并不是很整洁

委托本身就是一个对象,因此如果您为匿名方法创建委托,并将其交给其他方法执行,而不存储该委托以供将来引用,则是的,这将产生垃圾

例如:

collection.ForEach(delegate(T item)
{
    // do something with item
});
在这种情况下,将创建一个新的委托对象,但在对
ForEach
的调用之外,它不会被引用,因此符合垃圾收集的条件

然而,调用委托本身不会产生垃圾,这一点比调用任何其他相同类型的方法都要严重。例如,如果调用一个接受
对象
参数的委托,并传入一个
Int32
值,则该值将被装箱,但如果以相同的方式调用普通方法,则会发生这种情况

因此,使用委托应该可以,但过度创建委托对象将是一个问题


编辑:这里有一篇关于Xbox和XNA内存管理的好文章:。请注意这句话:

那么如何控制GC延迟呢?与设备的NetCF一样,Xbox GC是非代的。这意味着每个集合都是托管堆上的完整集合。因此,我们发现GC延迟与活动对象的数量近似成线性关系……然后再加上堆压缩的成本。我们的基准测试表明,深层对象层次结构与浅层对象层次结构之间的差异可以忽略不计,因此最重要的是对象的数量。处理小对象往往比处理大对象更便宜


正如您所看到的,尽量避免创建大量不必要的对象,您应该会过得更好。

在桌面环境中垃圾实际上是免费的。你要担心的是你生产了多少非垃圾。记住垃圾收集器是如何工作的:它首先标记所有已知对象,然后清除所有活动对象上的标记并压缩活动对象。代价高昂的步骤是“取消活动对象的标记”。销毁垃圾很便宜;识别昂贵的活动对象,而这一成本取决于您拥有的活动对象的数量(及其参考拓扑的复杂性),而不是您拥有的死对象的数量

然而,在XBOX和其他紧凑型框架上,垃圾收集器运行非常频繁,并且在创建新分配时运行得更频繁,因此,您担心创建垃圾也是正确的。您既要保持活动集较小(以便使集合便宜),又不进行新分配(因为这会触发集合)

创建委托确实会分配内存,但调用委托无非是在类上调用名为Invoke的方法。委托只不过是一个具有名为Invoke的方法的类,该方法在被调用时会立即调用另一个方法


不管怎样,如果内存性能有问题,那么正确的做法是取出内存分析器并使用它来分析程序。随意地想知道这个或那个是否会分配内存就像用指甲剪除草一样;这需要很多时间,实际上并不能实现你的目标。使用探查器分析您的性能,查看问题所在,然后进行修复。

正如其他人已经指出的那样,委托创建会产生垃圾

在您的示例中,使用
params
参数可能也会生成垃圾

考虑在不使用params关键字的情况下提供重载

这就是为什么库方法中通常存在具有不同数量参数的重载以及使用
params
关键字的重载的原因:

是和否

调用简单委托不会在堆上分配任何内容, 但是创建一个委托会在堆上分配64个字节

为了避免GC,您可以预先创建委托

让我们验证一下:

using BenchmarkDotNet.Running;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<BenchmarkDelegate>();            
        }
    }
}
在下面的输出中,向右滚动以查看分配的内存/Op列:


我明白了,知道这一点很好,尽管尝试使用委托而不创建任何委托(加载时除外)会很有趣。C#中的任何东西都会以这种方式创建垃圾。我不建议避免使用短期引用。通常我也不会,但在Xbox上,在XNA平台上,如果你能推迟GC(即减少GC的运行次数)而不产生负面影响,这确实是一个好主意,因为如果GC发生得太频繁,会导致游戏的不稳定。因此,如果您想修复GC,了解它的作用以及它何时起作用是一个好主意。例如,在我展示的代码中,如果您多次运行ForEach循环,通常,如果这不会改变行为,那么您可能应该将委托存储在某个变量中。游戏优化通常不同于普通的桌面DB应用。我知道这很旧,但。。。如果你事先知道
using BenchmarkDotNet.Running;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<BenchmarkDelegate>();            
        }
    }
}
using BenchmarkDotNet.Attributes;

namespace Test
{
    [MemoryDiagnoser]
    public class BenchmarkDelegate
    {
        public delegate int GetInteger();

        GetInteger _delegateInstance;

        public BenchmarkDelegate()
        {
            _delegateInstance = WithoutDelegate;
        }

        [Benchmark]
        public int WithInstance() => RunDelegated(_delegateInstance);

        [Benchmark]
        public int WithDelegate() => RunDelegated(WithoutDelegate);

        public int RunDelegated(GetInteger del) => del();

        [Benchmark]
        public int WithoutDelegate() => 0;
    }
}
DefaultJob : .NET Core 2.2.1 (CoreCLR 4.6.27207.03, CoreFX 4.6.27207.03), 64bit RyuJIT
|          Method |       Mean |     Error |    StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|---------------- |-----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
|    WithInstance |  7.5503 ns | 0.0751 ns | 0.0702 ns |           - |           - |           - |                   - |
|    WithDelegate | 35.4866 ns | 1.0094 ns | 1.2766 ns |      0.0203 |           - |           - |                64 B |
| WithoutDelegate |  0.0000 ns | 0.0000 ns | 0.0000 ns |           - |           - |

       - |                   - |