Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# List.Clear();vs列表<;T>;=新名单<;T>;(); 垃圾收集性能_C#_.net_List_Garbage Collection - Fatal编程技术网

C# List.Clear();vs列表<;T>;=新名单<;T>;(); 垃圾收集性能

C# List.Clear();vs列表<;T>;=新名单<;T>;(); 垃圾收集性能,c#,.net,list,garbage-collection,C#,.net,List,Garbage Collection,我在做一个有很多人工智能的游戏,我注意到我的垃圾收集器每秒收集3-4次对象,这真的会影响性能,并在帧速率下降之前将敌人数量限制在120。我分析了代码,发现罪魁祸首是我的碰撞检测代码,其中我有一个函数被多次调用,一个帧正在生成轴上的投影列表。代码就像 public List<Vector2> foo() { List<Vector2> projections = new List<Vector2>(); // Calculate and ret

我在做一个有很多人工智能的游戏,我注意到我的垃圾收集器每秒收集3-4次对象,这真的会影响性能,并在帧速率下降之前将敌人数量限制在120。我分析了代码,发现罪魁祸首是我的碰撞检测代码,其中我有一个函数被多次调用,一个帧正在生成轴上的投影列表。代码就像

public List<Vector2> foo()
{
    List<Vector2> projections = new List<Vector2>();

    // Calculate and return projections
}
公共列表foo()
{
列表投影=新列表();
//计算和回报预测
}
还有一些其他函数在每次调用时都会创建新列表,我在每次函数调用时都会停止生成新列表,而是将向量列表存储为类的一个字段,这样我就不会在每次函数调用时都调用新列表。这将垃圾收集调用从每秒3-4次减少到每10-20秒一次,使我能够在帧速率下降之前在地图上找到400-500个敌人

新的功能看起来像:

public List<Vector2> foo()
{
    // projections is now a field of this class so we just clear it each function call
    projections.Clear();

    // Calculate and return projections
}
公共列表foo()
{
//投影现在是这个类的一个字段,所以我们只需在每次函数调用时清除它
投影。清除();
//计算和回报预测
}
我的问题是,为什么List.Clear方法产生的垃圾收集比List=new List()少得多?我做了一些研究,当你调用clear时,列表中的项会发生什么,当它们不再有引用时,它们似乎也应该被垃圾收集,所以我的项是在函数内部创建的,所以当该函数退出时,列表在下一次调用中被清除,前面的项目不也应该被垃圾收集吗

我的理解是,清除的项目应该像调用List=newlist一样进行垃圾收集。显然,当我切换到List.Clear()时,我的垃圾收集量急剧减少,这是另外一回事。下面是我用来尝试理解列表项发生了什么的链接

清除列表时,只需将计数设置为0并擦除现有内容(如果旧对象是引用类型,则旧对象可能可收集)。现有的缓冲区(
T[]
)被保留,并可重新用于下一组。我猜你通常在每一帧都有大致相同数量的投影,所以这意味着相同的阵列可以永远使用

但是:如果您新建了它,那么会分配一个新的默认大小的缓冲区-它从10或20开始,IIRC(编辑:现在看起来是4)。当您添加(
add
)项时,每当它用完空间时,就必须调整缓冲区的大小(它使用倍增算法,因此当您添加第21项时,它将倍增到40,以此类推)。如果您有很多投影,这可能意味着分配(和重新复制)很多次-因此您可以分配一组最终被丢弃的阵列。。。当然还有单个列表对象

正是这些被丢弃的、大小为4、8、16、32、64、128、256、512、1024等的“满时加倍”缓冲区,加上实际的列表对象本身,您看到它们正在被收集;不是你的游戏对象

特别是:
Vector2
是一个
struct
,我相信-所以:没有什么可收集的


那真的可以加起来

你是如何测试垃圾收集的?@john我正在使用visual studio 2015社区。我使用探查器来显示内存使用情况,它有一个生命周期,在这个生命周期中,每当调用垃圾收集器时,它都有一个指示器;每次它必须调整缓冲区的大小以添加新项时,它还必须将旧数据从旧缓冲区块复制到新缓冲区,这可能会变得昂贵;拥有一个大致合适大小的数组已经意味着你永远不必作为一个额外的问题来做这件事,与在不同的类中为我需要的每个列表拥有一个本地字段相比,对象池是更合适的解决方案吗?@Darcy field per instance很简单;联营需要大量的管理;两者都可以,这取决于使用情况。每个实例的一个优点是大小通常正好在帧之间;如果使用pool,就无法保证预先存在的缓冲区的大小,我可以说,将适当的初始容量传递给
列表的构造函数将实现相同的效果,而不需要实例字段。取决于其他因素,它可能更有效。在引擎盖下,调用clear只调用
数组。清除底层异常并将大小重置为0(容量保持不变)。与任何
列表
突变一样,它也会增加
\u版本
,以强制对活动枚举数执行异常。