想象一下这段代码:
int i=9999999;
while ( i > 1 )
{
string UnusedMemory="this is a string that eats some ram" + i.ToString();
i--;
}
如果仅在运行GC.Collect()
时删除了未引用的对象,则此代码应分配大量的ram,直到收集发生。但它根本没有分配大量内存,为什么呢?在IL级别上是否有某种“删除”?或者GC.Collect()
自动调用更快?我知道这是一个简单的例子,但是如果它更复杂并且在该代码块中访问了字符串,那么无论如何都不会吃掉很多ram。
编辑:我更改了示例,以便字符串始终是唯一的,因此无法“缓存”
答案 0 :(得分:8)
字符串常量在字符串池中“缓存”,因此每次都是相同的字符串。
如果您对字符串使用方法String.IsInterned
,那么它将返回true
,这意味着它包含在池中。
公共语言运行库自动维护一个名为的表 实习池,包含每个唯一的单个实例 在程序中声明的文字字符串常量,以及任何唯一的 您以编程方式添加的String实例。
实习池保存了字符串存储空间。如果您指定文字 字符串常量为几个变量,每个变量都设置为 在实习池中引用相同的常量而不是引用 几个具有相同值的String的不同实例。
此外,当需要内存或分配了足够的元素时,会自动调用GC,因此即使字符串没有被缓存,也不会有问题。
来自Fundamentals of Garbage Collection:
当满足下列条件之一时,就会发生垃圾收集:
是否可以更改此行为或对其进行优化? 是的,在某种程度上:Latency Modes
这个问题与以下内容非常相关: C# memory usage for creating objects in a for loop
答案 1 :(得分:3)
当从堆中分配对象,并且没有足够的可用空间时,垃圾收集器将运行。如果第一个堆的集合没有释放足够的内存,它将继续在下一级堆上执行集合。只有当所有堆的集合都没有释放足够的内存时,它才会从系统中分配更多的内存。
由于您的示例中有许多最近释放的对象,因此第一个堆的集合将始终释放足够的内存。你的循环会多次填满堆。您可以在循环之前和之后打印GC.CollectionCount(0)
以查看它运行的次数。