String VS Byte [],内存使用情况

Ede*_*een 12 c# memory string

我有一个使用大量字符串的应用程序.所以我有一些内存使用问题.我知道在这种情况下最好的解决方案之一是使用数据库,但我暂时不能使用它,所以我正在寻找其他解决方案.

在C#中,字符串存储在Utf16中,这意味着与Utf8(对于我的字符串的主要部分)相比,我丢失了一半的内存使用量.所以我决定使用utf8字符串的字节数组.但令我惊讶的是,这个解决方案在我的应用程序中占用了比简单字符串多两倍的内

所以我做了一些简单的测试,但我想知道专家的意见.

测试1:固定长度字符串分配

var stringArray = new string[10000];
var byteArray = new byte[10000][];
var Sb = new StringBuilder();
var utf8 = Encoding.UTF8;
var stringGen = new Random(561651);
for (int i = 0; i < 10000; i++) {
    for (int j = 0; j < 10000; j++) {
        Sb.Append((stringGen.Next(90)+32).ToString());
    }
    stringArray[i] = Sb.ToString();
    byteArray[i] = utf8.GetBytes(Sb.ToString());
    Sb.Clear();
}
GC.Collect();
GC.WaitForFullGCComplete(5000);
Run Code Online (Sandbox Code Playgroud)

内存使用情况

00007ffac200a510        1        80032 System.Byte[][]
00007ffac1fd02b8       56       152400 System.Object[]
000000bf7655fcf0      303      3933750      Free
00007ffac1fd5738    10004    224695091 System.Byte[]
00007ffac1fcfc40    10476    449178396 System.String
Run Code Online (Sandbox Code Playgroud)

我们可以看到,字节数组占用的内存空间减少了两倍,这里没有真正的惊喜.

测试2:随机大小的字符串分配(具有实际长度)

var stringArray = new string[10000];
var byteArray = new byte[10000][];
var Sb = new StringBuilder();
var utf8 = Encoding.UTF8;
var lengthGen = new Random(2138784);
for (int i = 0; i < 10000; i++) {
    for (int j = 0; j < lengthGen.Next(100); j++) {
        Sb.Append(i.ToString());
        stringArray[i] = Sb.ToString();
        byteArray[i] = utf8.GetBytes(Sb.ToString());
    }
    Sb.Clear();
}
GC.Collect();
GC.WaitForFullGCComplete(5000);
Run Code Online (Sandbox Code Playgroud)

内存使用情况

00007ffac200a510        1        80032 System.Byte[][]
000000be2aa8fd40       12        82784      Free
00007ffac1fd02b8       56       152400 System.Object[]
00007ffac1fd5738     9896       682260 System.Byte[]
00007ffac1fcfc40    10368      1155110 System.String
Run Code Online (Sandbox Code Playgroud)

字符串占用的空间少于字节数组内存空间的两倍.使用较短的字符串,我期待字符串的开销更大.但似乎相反的是,为什么呢?

测试3:与我的应用程序对应的字符串模型

var stringArray = new string[10000];
var byteArray = new byte[10000][];
var Sb = new StringBuilder();
var utf8 = Encoding.UTF8;
var lengthGen = new Random();
for (int i=0; i < 10000; i++) {
    if (i%2 == 0) {
        for (int j = 0; j < lengthGen.Next(100000); j++) {
            Sb.Append(i.ToString());
            stringArray[i] = Sb.ToString();
            byteArray[i] = utf8.GetBytes(Sb.ToString());
            Sb.Clear();
        }
    } else {
        stringArray[i] = Sb.ToString();
        byteArray[i] = utf8.GetBytes(Sb.ToString());
        Sb.Clear();
    }
}
GC.Collect();
GC.WaitForFullGCComplete(5000);
Run Code Online (Sandbox Code Playgroud)

内存使用情况

00007ffac200a510        1        80032 System.Byte[][]
00007ffac1fd02b8       56       152400 System.Object[]
00007ffac1fcfc40     5476       198364 System.String
00007ffac1fd5738    10004       270075 System.Byte[]
Run Code Online (Sandbox Code Playgroud)

这里的字符串占用的内存空间比字节少得多.这可能会令人惊讶,但我认为空字符串只引用一次.是吗?但我不知道这是否可以解释所有巨大的差异.还有其他原因吗?什么是最好的解决方案?

das*_*ght 5

这可能会令人惊讶,但我认为空字符串只引用一次.

是的,一个空的StringBuilder回报string.Empty作为其结果.下面的代码片段打印True:

var sb = new StringBuilder();
Console.WriteLine(object.ReferenceEquals(sb.ToString(), string.Empty));
Run Code Online (Sandbox Code Playgroud)

但我不知道这是否可以解释所有巨大的差异.

是的,这完美地解释了它.您正在保存5,000个string对象.字节差异大约为270,000-(198,000/2),因此大约为170 kBytes.除以5,每个对象得到34个字节,大致相当于32位系统上指针的大小.

什么是最好的解决方案?

做同样的事情:让自己private static readonly空数组,并用它在每次得到的时间string.Emptysb.ToString():

private static readonly EmptyBytes = new byte[0];
...
else
{
    stringArray[i] = Sb.ToString();
    if (stringArray[i] == string.Empty) {
        byteArray[i] = EmptyBytes;
    } else {
        byteArray[i] = utf8.GetBytes(Sb.ToString());
    }
    Sb.Clear();
}
Run Code Online (Sandbox Code Playgroud)