【问题标题】:Will new local class instance be garbage collected if I return an array it creates?如果我返回它创建的数组,新的本地类实例会被垃圾收集吗?
【发布时间】:2015-10-21 20:25:23
【问题描述】:

假设一个类实例将构建数组:

class FooArrBuilder
{
    public Foo[] FinalResult { get { return arr; } }
    Foo[] arr;
    public void BuildArr();
    ...
}

那么它是这样使用的:

Foo[] GetFooArr()
{
    var fb = new FooArrBuilder();
    fb.BuildArr();
    ...
    return fb.FinalResult;
}

当一个数组被构建在一个函数的本地实例中时,该数组会被移出构建它的实例,还是整个实例都保存在内存中只是为了包含该数组?如果是后者,我不一定要复制数组 - 但也许我可以将类设为结构?感谢您的帮助:)

如果您愿意在此处详细说明 C# 的内存模型,它可能也会帮助我避免进一步的混淆。

提前致谢

【问题讨论】:

  • 您可能想了解value types and reference types 以及garbage collection 的工作原理。
  • Nitpick - 它永远不会被“丢弃”,因为它没有实现 IDisposable。您要查找的术语是收集的垃圾。即使这样,它也只是有资格进行垃圾收集-确切地何时收集它是无法确定的。
  • 简而言之,你的类和数组都是引用类型。只要一个对象是“可访问的”,即另一个活动对象有对它的引用,它也将保持活动状态。因此,只要您保留对数组的引用,它将保持活动状态。如果您保留对创建数组的对象的引用,它可能会或可能不会在未来某个不确定的时间点被收集。

标签: c# arrays memory


【解决方案1】:

是的,会的。只要Foo 类型的数组项不包含对FooArrBuilder 的引用

【讨论】:

    【解决方案2】:

    类实例不“拥有”或“包含”数组。它仅引用分配在堆上的数组。对该数组的其他引用将不会使该类的实例保持活动状态。

    如果任何其他对象(或堆栈帧)引用该数组,则为该数组分配的堆上的内存将保持为该数组分配。一旦这些对象被垃圾收集并且这些堆栈帧被弹出,垃圾收集器将声明为该数组分配的内存。

    【讨论】:

      【解决方案3】:

      我想我现在明白了。

      当 FooArrBuilder 被实例化时,它是在堆上完成的。 一个指向它的指针,一个 int 的大小,被放入堆栈。 堆上分配的对象的引用计数加一。

      当 FooArrBuilder 的实例创建数组时,几乎相同的过程发生在数组上;堆对象再次分配引用计数 1,这次是 Foo[] 类型。然而,这一次,没有指针放在堆栈上 - 相反,在我们之前检索到的堆栈指针指向的内存中,Foo[] arr,实际上是一个指向 Foo[] 对象的指针,被设置为指向到新数组。

      当我们即将返回时,我们从实例中检索另一个对 Foo[] 的引用,称为 arr。数组的引用计数现在为 2。 当函数返回时,对类实例的引用丢失,因此引用计数现在为 0。因此该实例被标记为垃圾。当实例(在某个不确定的点)稍后被垃圾收集时,它对数组的引用丢失 - 因此数组的引用计数再次为 1。

      【讨论】:

      • 这是一个很好的播放,但我相信垃圾收集器不会 count 引用。垃圾收集从根开始,遵循引用并构建活动对象图。如果此进程无法访问某个对象,则将其视为垃圾并进行回收。
      • 这有点复杂(正如@JerryFederspiel 提到的)。如果您真的想深入了解幕后发生的事情,我推荐CLR via C#,它有一个关于托管堆和垃圾收集的重要章节。
      猜你喜欢
      • 2020-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-09
      • 2020-05-11
      相关资源
      最近更新 更多