Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 字符串如何在堆中分配内存?_C#_Vb.net_Heap Memory_Dynamic Memory Allocation - Fatal编程技术网

C# 字符串如何在堆中分配内存?

C# 字符串如何在堆中分配内存?,c#,vb.net,heap-memory,dynamic-memory-allocation,C#,Vb.net,Heap Memory,Dynamic Memory Allocation,在创建String类的对象时,我对内存分配感到困惑。我创建了一个示例应用程序,它演示了在声明string对象时分配了多少内存。然后,我尝试增加字符串的长度,以查看堆中总消耗内存的差异 我的测试代码在这里 static void Main(string[] args) { long l1 = GC.GetTotalMemory(false); long l2 = 0; Console.WriteLine(l1.ToString()); myFunc();

在创建String类的对象时,我对内存分配感到困惑。我创建了一个示例应用程序,它演示了在声明string对象时分配了多少内存。然后,我尝试增加字符串的长度,以查看堆中总消耗内存的差异

我的测试代码在这里

static void Main(string[] args)
{
    long l1 = GC.GetTotalMemory(false);
    long l2 = 0;

    Console.WriteLine(l1.ToString());

    myFunc();

    l2 = GC.GetTotalMemory(false);
    Console.WriteLine(l2.ToString());
    Console.WriteLine(String.Format("Difference : {0}", (l2-l1)));
    Console.ReadKey();
}

private static void myFunc()
{
    String str = new String('a', 1);
}
当我执行以下代码时,会出现输出:

775596 //Memory at startup
816556 //After executing function
Difference : 40960
对于字符串长度0到2727,上述输出是相同的。例如,即使我创建了长度为2727的string对象,结果也与上面相同

String str = new String('a', 2727);
但是,当我再增加一个值并为2728创建一个字符串时,输出就不同了

775596 //Memory at startup
822780 //After executing function
Difference : 47184
我也在VB.Net控制台应用程序中尝试过它。在VB.Net中,0到797长度的字符串的输出是相同的。但是,当我将值增加到798时,它会发生变化

我不知道它是如何根据字符串的长度分配内存的

字符数组(字符串)表示它有2727个97字节的项(用于字符“a”)。我认为它将值与字符字节相乘。我知道字符类型的固定长度为256字节。但是,我只是想知道为什么会这样?因此,我还尝试将字符从“a”改为“z”。但是,结果与预期一致

有人能清楚地描述一下,当声明任何字符串或其他类对象时,内存是如何分配的吗

来自:

检索当前认为要分配的字节数

换句话说,此方法返回的值不是实际分配的所有字节的精确计算

我不知道该方法的具体实现,但如果发现有一些低优先级的进程正在监视堆中的高水位线以提供有问题的值,我也不会感到惊讶。(顺便说一句,对我来说,有趣的巧合是,你的第一个差异是2^12*10)

请注意,返回值中的这种不精确性实际上并没有告诉您“内存是如何分配的”。我不确定您的问题是否真的只是“为什么这个值没有改变我期望的方式”,或者您是否正在寻找一个更详细的解释,说明在.NET中通常是如何分配对象的

但如果你想了解更多关于后者的信息,实际上有一些非常好的文章,包括杰弗里·里克特(Jeffrey Richter)在MSDN上的这两篇:

它们有点老了,没有涵盖GC中的一些新特性,但基本内容在AFAIK中并没有真正改变,而且这些文章是永恒的

简短的版本是,对于
字符串
类型,因为它是不可变的,所以可以直接根据字符串的长度分配字符串的缓冲区(请注意,这与诸如
List
StringBuilder
之类的类不同,后者具有更复杂的数据结构,因此以更复杂的方式使用.NET内存管理器)

由于.NET内存管理器的工作方式,对象的新分配只需查看指向堆分配部分当前端的指针,将其用于新对象,并将指针移动到已分配的字节数

(字符串类型在.NET中是一种非常特殊的类型,因为它获得了本机代码支持和内部缓冲区的特殊处理,但在堆上分配的基本思想仍然适用)

同样,这些都不能解释你所看到的行为,但它回答了一个更广泛的问题,即内存分配是如何发生的


回到
GC.getTotalMemy()
方法的问题上,我确实从现在已经不存在的关于.NET的新闻组中找到了这个有趣的讨论,该新闻组存档在《个人电脑评论》的网站上(可能还有其他地方,但我就是在这里找到的):
.讨论有点曲折,我不认为它真正解决了你提出的问题。但无论如何,你可能会发现它是一本有趣的读物。

我看到的唯一问题是你的研究方法

int[] lengths = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
string[] strs = new string[lengths.Length];
long[] deltaMemory = new long[lengths.Length];

// We preload the functions we will use
var str0 = new string('A', 1);
var length0 = str0.Length;
long totalMemory0 = GC.GetTotalMemory(true);
long lastTotalMemory = totalMemory0;

for (int i = 0; i < lengths.Length; i++)
{
    strs[i] = new string((char)('A' + i), lengths[i]);
    long totalMemory = GC.GetTotalMemory(true);
    deltaMemory[i] = totalMemory - lastTotalMemory - lengths[i] * 2;
    lastTotalMemory = totalMemory;
}

Console.WriteLine("IntPtr.Size: {0}", IntPtr.Size);
for (int i = 0; i < lengths.Length; i++)
{
    Console.WriteLine("For size: {0}, extra memory: {1}", strs[i].Length, deltaMemory[i]);
}

因此,每个字符串(64位)分配了额外的26-32字节。Mmmh…我看到Skeet甚至写了一篇关于内存分配的博文:

您可以随时检查源代码。另外,请记住,您实际上在内存检查之间分配了2个字符串,而不仅仅是一个。
Console.WriteLine(l1.ToString());
也在分配字符串。请记住,字符不是字节!1个字符=2个字节
IntPtr.Size: 8
For size: 1, extra memory: 30
For size: 2, extra memory: 28
For size: 3, extra memory: 26
For size: 4, extra memory: 32
For size: 5, extra memory: 30
For size: 6, extra memory: 28
For size: 7, extra memory: 26
For size: 8, extra memory: 32
For size: 9, extra memory: 30
For size: 10, extra memory: 28
For size: 11, extra memory: 26
For size: 12, extra memory: 32
For size: 13, extra memory: 30
For size: 14, extra memory: 28
For size: 15, extra memory: 26
For size: 16, extra memory: 32
For size: 17, extra memory: 30
For size: 18, extra memory: 28
For size: 19, extra memory: 26
For size: 20, extra memory: 32
For size: 21, extra memory: 30
For size: 22, extra memory: 28
For size: 23, extra memory: 26
For size: 24, extra memory: 32
For size: 25, extra memory: 30
For size: 26, extra memory: 28
For size: 27, extra memory: 26
For size: 28, extra memory: 32
For size: 29, extra memory: 30
For size: 30, extra memory: 28
For size: 31, extra memory: 26
For size: 32, extra memory: 32
For size: 64, extra memory: 32
For size: 128, extra memory: 32
For size: 256, extra memory: 32
For size: 512, extra memory: 32
For size: 1024, extra memory: 32
For size: 2048, extra memory: 32
For size: 4096, extra memory: 32