c#如何移除循环中分配的内存?

本文关键字:分配 内存 循环 何移 | 更新日期: 2023-09-27 18:07:51

想象一下下面的代码:

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

如果未引用的对象只是在运行GC.Collect()时偶尔被删除,则该代码应该分配大量ram,直到collect发生。但是它并没有分配巨大的内存,为什么呢?是否在IL级别上实现了某种"删除"?还是自动调用GC.Collect()更快?我知道这是一个微不足道的例子,但如果它更复杂,并且在该代码块中访问字符串,它无论如何也不会消耗大量内存。

编辑:我改变了这个例子,使字符串总是唯一的,所以它不能被"缓存"

c#如何移除循环中分配的内存?

字符串常量被"缓存"在字符串池中,所以每次都是相同的字符串。

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

从字符串。IsInterned:

公共语言运行库自动维护一个名为内部池,其中包含每个惟一的单个实例在程序中声明的字面值字符串常量,以及任何惟一的以编程方式添加的字符串的实例。

内部池保存字符串存储空间。如果你赋值一个字面量字符串常量为多个变量,每个变量设置为引用内部池中相同的常量,而不是引用具有相同值的String的几个不同实例

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

选自《垃圾收集基础》:

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

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

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

这个问题与:在for循环中创建对象的c#内存使用

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

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