2015-08-04 51 views
12

Çok sayıda dizeyi kullanan bir uygulamam var. Yani bazı bellek kullanımı problemim var. Bu durumda en iyi çözümlerden birinin bir DB kullanmak olduğunu biliyorum, ancak şimdilik bunu kullanamıyorum, bu yüzden başkaları için çözümler arıyorum.Dize VS Byte [], bellek kullanımı

C# dizesinde, Utf16'da depolanıyor, yani bellek kullanımının yarısını Utf8 ile karşılaştırıyorum (dizelerimin büyük kısmı için). Bu yüzden, utf8 dizesinin bayt dizisini kullanmaya karar verdim. Ama benim için sürprizim bu çözüm benim uygulamada basit dizelerden iki kat daha fazla bellek alanı aldı.

Bazı basit testler yaptım, ancak uzmanların görüşlerini tam olarak bilmek istiyorum.

Testi 1: Sabit uzunluk dizeleri tahsis

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

Bellek Kullanımı

00007ffac200a510  1  80032 System.Byte[][] 
00007ffac1fd02b8  56  152400 System.Object[] 
000000bf7655fcf0  303  3933750  Free 
00007ffac1fd5738 10004 224695091 System.Byte[] 
00007ffac1fcfc40 10476 449178396 System.String 

Gördüğümüz gibi, diziler iki kez daha az bellek alanı, burada gerçek sürpriz almak bayt.

Testi 2: (gerçekçi uzunluğunda) Rastgele boyut dize tahsisi

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

Bellek Kullanımı

00007ffac200a510  1  80032 System.Byte[][] 
000000be2aa8fd40  12  82784  Free 
00007ffac1fd02b8  56  152400 System.Object[] 
00007ffac1fd5738  9896  682260 System.Byte[] 
00007ffac1fcfc40 10368  1155110 System.String 

Dize iki saatten daha bayt dizisinin hafıza alanını biraz daha az yer alır . Daha kısa dize ile dizeler için daha büyük bir yük bekleniyordu. Ama öyle görünüyor ki, tam tersi?

Testi 3: Uygulamam

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

Bellek Kullanımı Burada

00007ffac200a510  1  80032 System.Byte[][] 
00007ffac1fd02b8  56  152400 System.Object[] 
00007ffac1fcfc40  5476  198364 System.String 
00007ffac1fd5738 10004  270075 System.Byte[] 

dizeleri bayt çok daha az bellek alanını almaya gelen dize modeli. Bu şaşırtıcı olabilir, ama boş dize sadece bir kez atıfta bulunulduğunu farz ettim. Bu mu? Ama bu büyük farkın açıklanıp açıklanamayacağını bilmiyorum. Başka bir sebep mi var? En iyi çözüm nedir?

cevap

5

Bu şaşırtıcı olabilir, ancak boş dize yalnızca bir kez atıfta bulunulduğunu varsayalım. Sonuç olarak boş bir StringBuilder, string.Empty10 değerini döndürür. Evet, boş bir StringBuilder, sonuç olarak string.Empty değerini döndürür. baskılar True aşağıda kod parçacığı: Bu bütün bu büyük bir fark açıklayabilir eğer

var sb = new StringBuilder(); 
Console.WriteLine(object.ReferenceEquals(sb.ToString(), string.Empty)); 

Ama bilmiyorum.

Evet, bu mükemmel bir şekilde açıklıyor. 5000 string nesnesinde tasarruf edersiniz. Baytlardaki fark kabaca 270.000- (198.000/2), yani yaklaşık 170 kBytes'dir.5'e bölerek, nesne başına 34 bayt alırsınız; bu, kabaca 32 bitlik bir sistemdeki bir işaretçinin boyutudur.

En iyi çözüm nedir?

aynı şeyi yapın: Kendinizi bir private static readonly boş dizi yapmak ve bunu sen sb.ToString() den string.Empty olsun her zaman kullanın:

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

'kullanmak string.IsNullOrEmpty (stringArray [i])' değildir Neden ? –

+0

@MarkJansen Bu sadece bir örnek: 'stringArray [i]' in 'else 'dalında' if (i% 2 == 0)' koşulunda boş olduğundan emin olduğumu biliyorum, böylece karşılaştırmayı atlayabilirdim 'string.Empty' tamamen. – dasblinkenlight

+0

İlginç, gerçekten boş bir bayt referans kullanın bellek kullanımını çok artırın. Gönderiimde, 64 bitlik ve birim bayt olduğunu söylemeyi unuttum. Her neyse bu açıklama fikrinizi değiştirmez, bir işaretçi için 34 byte olsa bile (her dizenin 26 baytlık ek yükü ile daha fazla) bulsam bile. System.Byte [] [] ile 10K işaretçisinin boyutunu (80032 KB, yani kullanışlı bellek boyutunun% 25'i) kaybettim. Bu kadar dolaylı kullanımdan kaçınmanın bir yolu var mı? Belki de bayt dizisi ile değil. – Edeen