3

想象一下这段代码:

int i=9999999;
while ( i > 1 )
{
    string UnusedMemory="this is a string that eats some ram" + i.ToString();
    i--;
}

如果仅在运行时才删除未引用的对象GC.Collect(),则此代码应分配大量内存,直到发生收集。但它根本没有分配巨大的内存,为什么?是否在 IL 级别实施了某种“删除”?还是GC.Collect()自动调用更快?我知道这是一个微不足道的例子,但如果它更复杂并且在该代码块中访问字符串,它无论如何也不会吃很多内存。

编辑:我更改了示例,使字符串始终是唯一的,因此不能“缓存”

4

2 回答 2

8

字符串常量被“缓存”在字符串池中,因此每次都是相同的字符串。

如果您String.IsInterned在字符串上使用该方法,那么它将返回true,这意味着它包含在池中。

来自String.IsInterned

公共语言运行时自动维护一个称为实习池的表,其中包含程序中声明的每个唯一文字字符串常量的单个实例,以及您以编程方式添加的任何唯一 String 实例。

实习生池保存字符串存储。如果将文字字符串常量分配给多个变量,则每个变量都设置为引用实习池中的相同常量,而不是引用具有相同值的多个不同 String 实例。

此外,当需要内存或分配了足够的元素时,会自动调用 GC,因此即使没有缓存字符串也不会成为问题。

垃圾收集的基础知识

当下列条件之一为真时,就会发生垃圾收集:

  • 系统物理内存不足。
  • 托管堆上分配的对象使用的内存超过了可接受的阈值。这意味着已超过托管堆上可接受的内存使用量的阈值。随着流程的运行,此阈值会不断调整。
  • GC.Collect 方法被调用。在几乎所有情况下,您都不必调用此方法,因为垃圾收集器会持续运行。此方法主要用于特殊情况和测试。

是否有可能改变这种行为或优化它? 是的,在某种程度上:延迟模式

这个问题非常相关: C# memory usage for creating objects in a for loop

于 2013-04-06T09:52:09.293 回答
3

当从堆中分配一个对象,并且没有足够的可用空间时,垃圾收集器将运行。如果第一个堆的集合没有释放足够的内存,它将继续在下一级堆上进行集合。只有当所有堆的集合没有释放足够的内存时,它才会从系统中分配更多的内存。

由于您的示例中有很多最近释放的对象,因此第一个堆的集合将始终释放足够的内存。您的循环将多次填充堆。您可以在循环之前和之后打印出来GC.CollectionCount(0),看看它运行了多少次。

于 2013-04-06T10:09:15.270 回答