虽然字符串的最大大小为 ~0.75GB,但 Visual Studio 显示 5GB 的使用量和内存运行,为什么会有差异?
Whilst the maximum size of the string is ~0.75GB, Visual Studio shows 5GB of usage and runs of memory, why the difference?
这段代码没有做任何实际的事情我只是看看会发生什么。
据我所知,唯一保留的两个变量是(最终)巨大的字符串,以及跟踪字符串长度的可忽略大小的 int。
在我的机器上,字符串大约为 0.75GB,此时 OutOfMemoryException
出现。在此阶段 Visual Studio 显示大约 5GB 的使用量。所以我想知道为什么会有差异。
var initialText = "Test content";
var text = initialText;
var length = text.Length;
while (true)
{
try
{
var currentLength = text.Length;
Console.WriteLine($"Current Length - {currentLength}");
Console.WriteLine($"Current Size in GB - {System.Text.Encoding.UTF8.GetByteCount(text)/1024.0/1024.0/1024.0}");
text = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text));
Console.WriteLine($"Change In Size - {currentLength / (length + 0.0)}");
length = currentLength;
}
catch (OutOfMemoryException)
{
break;
}
}
作为第二个问题,当我开始 运行 代码时,根据任务管理器,我的机器有大约 11GB 的可用空间,当它遇到异常时,它增加了大约 3GB,这并没有与上述数字相符。有什么想法吗?
首先,.net中的字符串是一个UTF-16字序列,所以每个字符占用2个字节。要获得内存中字符串的大小(以字节为单位),您需要将其长度乘以 2(忽略 CLR 实例 header)。
Console.WriteLine($"Current Size in GB - {text.Length * 2.0 /1024/1024/1024}");
另一个限制是 .NET 中的数组大小,如@TheGenral 所述,请阅读备注 here。您可以达到 2 个限制:最大大小 (2GB) 和最大索引。
以下是您测试的修改版本:
var text = "Test content";
long length = text.Length;
try
{
while (true)
{
var currentLength = text.Length;
Console.WriteLine($"Current Length - {currentLength}");
Console.WriteLine($"Current Size in GB - {text.Length * 2.0 / 1024 / 1024 / 1024}");
text += new string('a', 500 * 1024*1024);
length = currentLength;
GC.Collect();
}
}
catch (OutOfMemoryException e)
{
Console.WriteLine(e);
}
StringBuilder
版本差异:
var text = new StringBuilder("Test content");
...
text.Append('a', 500 * 1024*1024);
如果您不启用 gcAllowVeryLargeObjects,那么您将获得带有 1B 元素的 OOM。
我无法使用字符串连接获得 2B 个元素,但如果您使用 StringBuilder
重做此测试,那么您可以达到 2B 个字符。在这种情况下,您将遇到第二个限制:数组不能容纳超过 20 亿个元素。 Here是关于上限的讨论。
在 this thread 中讨论了最大字符串长度。
如果您在 Release
模式下 运行 此代码,您将看到进程内存消耗几乎等于控制台输出中的字符串大小。
我注意到但无法解释的另一件有趣的事情是,在 StringBuilder
、gcAllowVeryLargeObjects
和 Debug
模式下,我可以达到 4GB,但在 Release
模式下它几乎达到 3GB。欢迎评论为什么会这样:)
这段代码没有做任何实际的事情我只是看看会发生什么。
据我所知,唯一保留的两个变量是(最终)巨大的字符串,以及跟踪字符串长度的可忽略大小的 int。
在我的机器上,字符串大约为 0.75GB,此时 OutOfMemoryException
出现。在此阶段 Visual Studio 显示大约 5GB 的使用量。所以我想知道为什么会有差异。
var initialText = "Test content";
var text = initialText;
var length = text.Length;
while (true)
{
try
{
var currentLength = text.Length;
Console.WriteLine($"Current Length - {currentLength}");
Console.WriteLine($"Current Size in GB - {System.Text.Encoding.UTF8.GetByteCount(text)/1024.0/1024.0/1024.0}");
text = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text));
Console.WriteLine($"Change In Size - {currentLength / (length + 0.0)}");
length = currentLength;
}
catch (OutOfMemoryException)
{
break;
}
}
作为第二个问题,当我开始 运行 代码时,根据任务管理器,我的机器有大约 11GB 的可用空间,当它遇到异常时,它增加了大约 3GB,这并没有与上述数字相符。有什么想法吗?
首先,.net中的字符串是一个UTF-16字序列,所以每个字符占用2个字节。要获得内存中字符串的大小(以字节为单位),您需要将其长度乘以 2(忽略 CLR 实例 header)。
Console.WriteLine($"Current Size in GB - {text.Length * 2.0 /1024/1024/1024}");
另一个限制是 .NET 中的数组大小,如@TheGenral 所述,请阅读备注 here。您可以达到 2 个限制:最大大小 (2GB) 和最大索引。
以下是您测试的修改版本:
var text = "Test content";
long length = text.Length;
try
{
while (true)
{
var currentLength = text.Length;
Console.WriteLine($"Current Length - {currentLength}");
Console.WriteLine($"Current Size in GB - {text.Length * 2.0 / 1024 / 1024 / 1024}");
text += new string('a', 500 * 1024*1024);
length = currentLength;
GC.Collect();
}
}
catch (OutOfMemoryException e)
{
Console.WriteLine(e);
}
StringBuilder
版本差异:
var text = new StringBuilder("Test content");
...
text.Append('a', 500 * 1024*1024);
如果您不启用 gcAllowVeryLargeObjects,那么您将获得带有 1B 元素的 OOM。
我无法使用字符串连接获得 2B 个元素,但如果您使用 StringBuilder
重做此测试,那么您可以达到 2B 个字符。在这种情况下,您将遇到第二个限制:数组不能容纳超过 20 亿个元素。 Here是关于上限的讨论。
在 this thread 中讨论了最大字符串长度。
如果您在 Release
模式下 运行 此代码,您将看到进程内存消耗几乎等于控制台输出中的字符串大小。
我注意到但无法解释的另一件有趣的事情是,在 StringBuilder
、gcAllowVeryLargeObjects
和 Debug
模式下,我可以达到 4GB,但在 Release
模式下它几乎达到 3GB。欢迎评论为什么会这样:)