繁体   English   中英

C# - 垃圾收集

[英]C# - Garbage Collection

好的,所以我理解堆栈和堆(堆栈上的值,堆上的引用)。

当我声明一个类的新实例时,它会存在于堆上,并在堆栈的内存中引用此点。 我也知道C#是自己的垃圾收集(即它确定何时不再使用实例化的类并回收内存)。

我有两个问题:

  1. 我对垃圾收集的理解是否正确?
  2. 我可以自己做吗? 如果是这样,我自己做这件事有什么好处,或者我应该离开它。

我问,因为我在For循环中有一个方法。 每次循环时,我都会创建一个新类的实例。 在我的脑海中,我把所有这些课程都放在一堆,没有做任何事情,只是占用记忆,我想尽快摆脱它们以保持整洁!

我是正确理解这个还是我错过了什么?

好的,所以我理解堆栈和堆(堆栈上的值,堆上的引用

我认为你不了解堆栈和堆。 如果值存在于堆栈中那么整数数组在哪里? 整数是价值观。 你是否告诉我整数数组将其整数保留在堆栈中? 当你从一个方法中返回一个整数数组时,比如说,其中有一万个整数,你是告诉我那些一万个整数被复制到堆栈中了吗?

当它们存在于堆栈中时,值存在于堆栈中,并且当它们存在于堆上时它们存在于堆上。 事物的类型 与其存储的生命周期有关的想法是无稽之谈。 短期存储位置在堆栈中; 长期存储的存储位置在堆上,并且与它们的类型无关。 一个长期存在的int必须在堆上,与一个类的长期实例相同。

当我声明一个类的新实例时,它会存在于堆上,并在堆栈的内存中引用此点。

为什么引用必须进入堆栈? 同样, 引用存储的生命周期与其类型无关 如果引用的存储是长期存储的,则引用将在堆上进行。

我也知道C#是自己的垃圾收集(即它确定何时不再使用实例化的类并回收内存)。

C#语言没有这样做; CLR这样做了。

我对垃圾收集的理解是否正确?

你似乎相信很多关于堆栈和堆的谎言,所以赔率不好,不是。

我可以自己做吗?

不是在C#中,没有。

我问,因为我在For循环中有一个方法。 每次循环时,我都会创建一个新类的实例。 在我的脑海中,我把所有这些课程都放在一堆,没有做任何事情,只是占用记忆,我想尽快摆脱它们以保持整洁!

垃圾收集的全部目的是让您免于担心整理。 这就是为什么它被称为“自动垃圾收集”。 它为你整理。

如果您担心自己的循环会产生收集压力 ,并且出于性能原因希望避免收集压力,那么我建议您采用合并策略。 明确的汇集策略开始是明智的; 那是:

while(whatever)
{
    Frob f = FrobPool.FetchFromPool();
    f.Blah();
    FrobPool.ReturnToPool(f);
}

而不是尝试使用复活终结器进行自动池化。 除非你是终结语义专家,否则我建议一般反对终结者和对象复活。

如果池中没有池,则该池当然会分配一个新的Frob。 如果池中有一个,那么它将其移出并将其从池中移除直到它被放回。(如果你忘记将Frob放回池中,GC最终会到达它。)通过追求一个池策略,你导致GC最终将所有Frobs移动到第2代堆,而不是在第0代堆中创建大量的收集压力。 然后收集压力消失,因为没有分配新的Frobs。 如果其他东西正在产生收集压力,那么Frobs就可以安全地进入第2代堆,很少有人访问它们。

这当然与您描述的策略完全相反; 汇集策略的重点是让对象永远存在 对象游逛永远是,如果你打算使用它们一件好事。

当然,在通过分析知道由于收集压力导致性能问题之前,请不要进行这些更改! 在桌面CLR上很少出现这样的问题; 它在紧凑型CLR上更为常见。

更一般地说,如果你是那种让记忆管理员按照时间表清理你感到不舒服的人,那么C#就不适合你。 考虑C而不是。

值存在于堆栈上,堆上的引用

这是一个实现细节 没有什么可以阻止.NET Framework将两者都存储在堆栈中。

我也知道C#是自己的垃圾收集

C#与此无关。 这是CLR提供的服务。 VB.NET,F#等都还有垃圾收集。

如果它没有根强CLR 从内存中删除的对象。 例如,当您的类实例超出for循环范围时。 会有一些躺在附近,但他们最终都会被回收,无论是由垃圾收集或程序终止。

我可以自己做吗? 如果是这样,我自己这样做有什么好处,还是我应该离开呢?

您可以使用GC.Collect强制收集。 你不应该这样做,因为这是一项昂贵的操作。 让一些物品占用内存的时间比绝对需要的时间长一些。 GC非常擅长自己的工作。 你也会强迫短命的物体升级到他们不会正常的世代。

首先, 关于价值类型真相的 Erics开创性帖子

其次,关于垃圾收集,收集器对你运行程序的了解远远超过你所知道的,不要试图再次猜测它,除非你遇到内存泄漏的极不可能的情况。

所以对于你的第二个问题,不要试图“帮助”GC。

我会在CG上找到一个这样的帖子并更新这个答案。

我可以自己做吗? 如果是这样,我自己做这件事有什么好处,或者我应该离开它。

是的,你可以使用GC.Collect 但你不应该 GC针对短期变量,方法变量和长寿命变量进行了优化,这些变量通常会在应用程序的生命周期内保持不变。

介于两者之间的变量并不常见,对GC来说并不是最佳选择。

通过强制GC.Collect,你更有可能导致范围内的变量被强制进入中间状态,这与你试图完成的情况相反。

另请参阅MSDN文章编写高性能托管应用程序:入门

GC是自我调整的,可根据应用程序内存要求进行自我调整。 在大多数情况下,以编程方式调用GC将阻碍调整。 通过调用GC.Collect“帮助”GC很可能无法提高应用程序性能

您对垃圾收集的理解已经足够了。 从本质上讲,未引用的实例被视为超出范围且不再需要。 确定了这一点后,收集器将在未来某个时刻删除未引用的对象。

没有办法强制垃圾收集器只收集一个特定的实例。 您可以要求它执行正常的“收集所有可能的”操作GC.Collect() ,但您不应该这样做。 如果你把它留在自己的设备上,垃圾收集器是高效和有效的。

特别是它擅长收集寿命短的对象,就像那些作为临时对象创建的对象一样。 您不必担心在循环中创建对象的负载,除非它们具有阻止立即收集的长寿命。

请参阅有关堆栈和堆的相关问题

在您的特定场景中,同意,如果您在for循环中新建对象,那么您将获得次优性能。 对象是在循环中存储(或以其他方式使用),还是被丢弃? 如果是后者,你可以通过在循环外部新建一个对象并重新使用它来优化它吗?

关于可以实现自己的GC,C#中没有明确的删除关键字,您必须将其留给CLR。 然而,您可以给它提示,例如何时收集,或在收集期间忽略什么,但是除非绝对必要,否则我会留下。

最好的祝福,

阅读Microsoft的以下文章,以获得有关C#中垃圾收集的一定知识。 我相信它会帮助那些需要有关此事的信息的人。

.NET Framework中的内存管理和垃圾收集

如果您在编写C#时对代码中某些区域的性能感兴趣,则可以编写不安全的代码 您将获得性能加分,并且在您的固定块中,垃圾收集器很可能不会发生。

垃圾收集基本上是参考跟踪。 我想不出有什么理由要你改变它。 你发现内存没有被释放,你有什么问题吗? 或许您正在寻找处置模式

编辑:用“参考跟踪”替换“引用计数”,不要与对象引用/解除引用的增量/减量计数器混淆(例如从Python)。 我认为将对象图生成称为“Counting”是很常见的,就像在这个答案中一样: 为什么C#中没有引用计数+垃圾收集? 但我不会拿起(Eric)Eric Lippert的手套:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM