[英]C# - Garbage Collection
好的,所以我理解堆栈和堆(堆栈上的值,堆上的引用)。
当我声明一个类的新实例时,它会存在于堆上,并在堆栈的内存中引用此点。 我也知道C#是自己的垃圾收集(即它确定何时不再使用实例化的类并回收内存)。
我有两个问题:
我问,因为我在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,你更有可能导致范围内的变量被强制进入中间状态,这与你试图完成的情况相反。
GC是自我调整的,可根据应用程序内存要求进行自我调整。 在大多数情况下,以编程方式调用GC将阻碍调整。 通过调用GC.Collect“帮助”GC很可能无法提高应用程序性能
您对垃圾收集的理解已经足够了。 从本质上讲,未引用的实例被视为超出范围且不再需要。 确定了这一点后,收集器将在未来某个时刻删除未引用的对象。
没有办法强制垃圾收集器只收集一个特定的实例。 您可以要求它执行正常的“收集所有可能的”操作GC.Collect()
,但您不应该这样做。 如果你把它留在自己的设备上,垃圾收集器是高效和有效的。
特别是它擅长收集寿命短的对象,就像那些作为临时对象创建的对象一样。 您不必担心在循环中创建对象的负载,除非它们具有阻止立即收集的长寿命。
阅读Microsoft的以下文章,以获得有关C#中垃圾收集的一定知识。 我相信它会帮助那些需要有关此事的信息的人。
如果您在编写C#时对代码中某些区域的性能感兴趣,则可以编写不安全的代码 。 您将获得性能加分,并且在您的固定块中,垃圾收集器很可能不会发生。
垃圾收集基本上是参考跟踪。 我想不出有什么理由要你改变它。 你发现内存没有被释放,你有什么问题吗? 或许您正在寻找处置模式
编辑:用“参考跟踪”替换“引用计数”,不要与对象引用/解除引用的增量/减量计数器混淆(例如从Python)。 我认为将对象图生成称为“Counting”是很常见的,就像在这个答案中一样: 为什么C#中没有引用计数+垃圾收集? 但我不会拿起(Eric)Eric Lippert的手套:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.