在处理大字符串时,有没有办法在 C# 中手动释放内存?

Is there a way manually release memory in C# when handling large strings?

提问人:Ben Jamin 提问时间:10/28/2023 最后编辑:Ben Jamin 更新时间:10/28/2023 访问量:135

问:

目前,我正在做一个需要从套接字连接接收大量数据的项目。该实现是用 C++ 编写的,我正在使用 C# 包装器来处理连接。在包装器中,我使用 Encoding.UTF8.GetString() 从 byte[] 转换为字符串。问题来了,在 C# 中没有办法释放字符串内存,因此内存消耗总是越来越高。GC 无法收集内存,并且达到了不可接受的高级别。

我编写了一个随机字符串生成器来模仿这种行为:

Random rand = new Random();

// Choosing the size of string
int stringlen = 64 * 1024;
int round = 100000;
int randValue;
StringBuilder str = new(stringlen);
char letter;

for (int j = 0; j < round; j++)
{
    for (int i = 0; i < stringlen; i++)
    {

        // Generating a random number. 
        randValue = rand.Next(0, 26);

        // Generating random character by converting 
        // the random number into character. 
        letter = Convert.ToChar(randValue + 65);

        // Appending the letter to string. 
        str.Append(letter);
    }
}

下面是 dotTrace 中的内存使用情况:

内存洞察

有没有办法解决这个问题?

我希望我们可以像在cpp中一样使用delete()或free()方法来手动释放字符串

[编辑]我注意到我犯了一个错误,我没有清除 StringBuilder。为了更清楚起见,我修改了以下代码:

using System.Text;

namespace TestBigString
{
    internal class Program
    {
        private static void Main()
        {
            const int round = 10000;

            StringBuilder sb = new();

            for (var j = 0; j < 1000; j++)
            {
                for (var i = 0; i < round; i++)
                {
                    sb.Append(RandomString());
                }

                sb.Clear();
            }
        }

        private static string RandomString()
        {
            Random rand = new();

            // Choosing the size of string
            const int stringLen = 64 * 1024;
            StringBuilder str = new(stringLen);

            for (var i = 0; i < stringLen; i++)
            {
                // Generating a random number. 
                var randValue = rand.Next(0, 26);

                // Generating random character by converting 
                // the random number into character. 
                var letter = Convert.ToChar(randValue + 65);

                // Appending the letter to string. 
                str.Append(letter);
            }
            return str.ToString();
        }
    }
}

记忆消耗的GIF

由于每轮都生成了 64KB 的字符串,并且我运行了 10000 轮,因此我预计只有大约 640MB 的内存。然而,我得到的远不止这些。

C# 字符串 内存管理 大数据

评论

0赞 Auditive 10/28/2023
也许在这篇文章中,您会找到解释或答案:stackoverflow.com/questions/2423111/......
0赞 Auditive 10/28/2023
此外,也许您想使用 Streams 并直接从网络读取到文件中,而无需将字符串存储在内存中。然后在 C++ 应用程序中从文件中读取数据。由于额外的 I/O 操作,它可能会降低内存使用量和性能。
1赞 Flydog57 10/28/2023
深入了解内存问题的正确方法是使用性能监视器 (PerfMon) 和“.NET 内存”计数器。如果字符串大于 85kb,则它们是大型对象,并在单独的堆 (LOH) 中进行管理。LOH 集合始终是 Gen2 集合,这可能就是您看到已发布的原因。如果可以将套接字数据流式传输到较小的缓冲区中,则可能会看到很大的改进
0赞 Fildor 10/28/2023
另一个问题是:为什么要首先从 C++ 过渡到 C#/.Net?仅此一项就会导致各种问题。
0赞 shingo 10/28/2023
模仿并没有占据我太多的记忆。是否生成了自己的 .net 运行时,或者是否设置了非常大的内存压力值?查看屏幕截图,就好像 GC 不起作用一样。

答: 暂无答案