GC 如何与 IEnumerator 一起工作并产生

本文关键字:工作 一起 IEnumerator GC | 更新日期: 2023-09-27 18:34:29

我知道枚举器和yield关键字可用于帮助进行异步/交错操作,因为您可以调用MoveNext()来运行下一个代码块。

但是,我真的不明白枚举器对象是什么。枚举器作用域使用的内存去哪儿了?如果您不一直MoveNext()枚举器,它最终会得到 GC 吗?

基本上,我试图降低我的 GC 命中率,因为我可能使用了很多枚举器,而 GC 可能是 Unity 内部的一个问题,特别是由于它使用的旧版本的 Mono。

我试图对此进行剖析,但仍然无法理解它们。我不明白枚举器发生的范围/引用。我也不明白当您从生成的函数创建一个枚举器时,枚举器是否被创建为对象。

以下示例更好地显示了我的困惑:

// Example enumerator
IEnumerator<bool> ExampleFunction()
{
    SomeClass heavyObject = new SomeClass();
    while(heavyObject.Process())
    {
        yield return true;
    }
    if(!heavyObject.Success)
    {
        yield return false;
    }
    // In this example, we'll never get here - what happens to the incomplete Enumerator
    // When does heavyObject get GC'd?
    heavyObject.DoSomeMoreStuff();
}
// example call - Where does this enumerator come from? 
// Is something creating it with the new keyword in the background?
IEnumerator<bool> enumerator = ExampleFunction();
while(enumerator.MoveNext())
{
    if(!enumerator.Current)
    {
        break;
    }
}
// if enumerator is never used after this, does it get destroyed when the scope ends, or is it GC'd at a later date?

GC 如何与 IEnumerator 一起工作并产生

您可能应该阅读枚举器内部帖子。从内部,您可以回答所有这些问题。

速成课程:每个迭代器方法执行都会返回一个新的枚举器对象。局部变量成为字段。

如果没有人再使用该枚举器对象,则它有资格进行收集。此外,出于 GC 目的,局部变量创建的所有引用都会消失。

对于局部变量何时不再是 GC 引用,有一些边缘情况。如果您的枚举器寿命相当短,这无关紧要。

CLR 不知道枚举器是什么。它只是 C# 编译器生成的一个类。

xanatos的链接拉到这个答案中,因为它是说明性的,他似乎没有发布答案:http://goo.gl/fs4eNo

如果在此之后从未使用枚举器,那么它在以下情况下是否会被销毁 范围结束

正确

收集的资格是没有参考资料。对于 GC 的范围界定,枚举器没有什么特别之处。当枚举器超出范围时,它们以与任何其他类型相同的方式处置/清除。

枚举器的管理方式与所有其他托管实例相同 - 当枚举器超出范围时。因此,如果从未调用MoveNext()则 GC 会在枚举器超出其范围时删除(或更好地将其标记为删除(。迭代枚举器不会以任何并行方式发生,因此执行和垃圾回收在完成后都会确定和按顺序运行。

在迭代器方法中,yield-return -stament 根本不是最后一条语句,它只是意味着当调用MoveNext()时,应该返回当前结果。但是,yield return后面的所有内容都在调用MoveNext之后运行,因此您的DoSomeMoreStuff调用也是如此。

但是,您的heavyObject具有该方法的范围,因此它与迭代器(其中迭代器是while循环中的块(一样长。这意味着,如果您的迭代器甚至没有返回任何实例heavyObject将立即被释放,否则迭代完成后。