【问题标题】:Memory usage of strings (or any other objects) in .Net.Net 中字符串(或任何其他对象)的内存使用情况
【发布时间】:2011-03-02 14:12:29
【问题描述】:

我写了这个小测试程序:

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();
        }
    }
}

Release build 中的输出是:

Currently using 18800KB.
Currently using 118664KB.
Leaked 99864KB.

我假设 GC.collect 调用将删除分配的字符串,因为它们超出了范围,但它似乎没有。我不明白,也找不到解释。也许有人在这里?

谢谢, 亚历克斯

【问题讨论】:

    标签: c# .net memory


    【解决方案1】:

    您正在查看私有内存大小 - 托管堆将扩展以容纳字符串,但当字符串被垃圾回收时,它不会将内存释放回操作系统。相反,托管堆会更大,但有很多可用空间 - 因此,如果您创建更多对象,则不需要扩展堆。

    如果您想查看托管堆中的内存使用,请查看GC.GetTotalMemory。请注意,由于垃圾收集的复杂性,所有这些都存在一定程度的模糊性。

    【讨论】:

    • 调用GC.Collect 是否真的会根据某个隐藏变量进行垃圾回收?
    • @Phil:我不会说它基于“隐藏变量”,但现在不能保证实际进行收集。如果您愿意,这是一个请求而不是命令。
    【解决方案2】:

    确实,我使用了私有内存大小,因为那是最接近 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 忽略了。

    【讨论】:

    • 您是否在调试器下运行过? (即使在发布版本中?)这会影响 GC。
    • 我是,但没有它会产生相同的结果。
    猜你喜欢
    • 1970-01-01
    • 2014-12-03
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    相关资源
    最近更新 更多