在 .Net 中,字符串(或任何其他对象)的内存使用情况。

6
我写了这个小测试程序:
using System;

namespace GCMemTest
{
    class Program
    {
        static void Main(string[] args)
        {
            System.GC.Collect();

            System.Diagnostics.Process pmCurrentProcess = System.Diagnostics.Process.GetCurrentProcess();

            long startBytes = pmCurrentProcess.PrivateMemorySize64;

            double kbStart = (double)(startBytes) / 1024.0;
            System.Console.WriteLine("Currently using " + kbStart + "KB.");

            {
                int size = 2000000;
                string[] strings = new string[size];
                for(int i = 0; i < size; i++)
                {
                    strings[i] = "blabla" + i;
                }
            }

            System.GC.Collect();

            pmCurrentProcess = System.Diagnostics.Process.GetCurrentProcess();
            long endBytes = pmCurrentProcess.PrivateMemorySize64;

            double kbEnd = (double)(endBytes) / 1024.0;
            System.Console.WriteLine("Currently using " + kbEnd + "KB.");

            System.Console.WriteLine("Leaked " + (kbEnd - kbStart) + "KB.");
            System.Console.ReadKey();
        }
    }
}

在发布版中的输出结果为:
Currently using 18800KB.
Currently using 118664KB.
Leaked 99864KB.

我假设GC.collect调用将会删除已分配的字符串,因为它们超出作用域,但看起来并不是这样。我不理解,也找不到解释。或许在这里有人能帮忙吗?
谢谢, Alex
2个回答

9
你正在查看私有内存大小 - 托管堆将会扩展以容纳字符串,但在垃圾回收时不会将内存释放回操作系统。相反,托管堆将变得更大,但有很多空闲空间 - 因此,如果您创建更多对象,它不需要扩展堆。
如果您想查看托管堆中使用的内存,请查看GC.GetTotalMemory。请注意,由于垃圾回收的复杂性,所有这些都存在一定程度的模糊性。

调用 GC.Collect 方法是否会执行垃圾回收取决于某些隐藏变量,这是真的吗? - Phil Gan
1
@Phil:我不会说它是基于“隐藏变量”,但不能保证现在实际进行收集。如果你愿意,它更像是一个请求而不是命令。 - Jon Skeet

0

实际上,我使用了私有内存大小,因为它最接近 Process Explorer 中的内存大小。

如果我像这样重写程序,使用 GC.GetTotalMemory:

using System;

namespace GCMemTest
{
    class Program
    {
        static void Main(string[] args)
        {
            System.GC.Collect();

            long startBytes = System.GC.GetTotalMemory(true);

            {
                string[] strings = new string[2000000];
                for (int i = 0; i < 2000000; i++)
                {
                    strings[i] = "blabla" + i;
                }
                strings = null;
            }

            System.GC.Collect();

            long endBytes = System.GC.GetTotalMemory(true);

            double kbStart = (double)(startBytes) / 1024.0;
            double kbEnd = (double)(endBytes) / 1024.0;

            System.Console.WriteLine("Leaked " + (kbEnd - kbStart) + "KB.");
            System.Console.ReadKey();
        }
    }
}

然后输出结果为:

泄露了0KB。

只有当我有“strings = null;”时才会出现这种情况,如果将其删除,则会泄漏100MB。这意味着主程序中的局部作用域不会导致数组被释放。如果我将该部分移动到静态方法Test中,并调用该方法,我会泄漏几个字节。我想我应该从中学到的是GC忽略了局部作用域。


你有没有在调试器下运行?(即使是在发布版本中?)这会影响垃圾回收。 - Jon Skeet
我曾经有它,但没有它也能得到相同的结果。 - ava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接