c#压缩字符串的方法

2020-06-24 18:02:36王冬梅

一:背景

1. 讲故事

在我们的一个全内存项目中,需要将一家大品牌店铺小千万的trade灌入到内存中,大家知道trade中一般会有订单来源,省市区 ,当把这些字段灌进去后,你会发现他们特别侵蚀内存,因为都是字符串类型,不知道大家对内存侵蚀性是不是很清楚,我就问一个问题。

Question: 一个空字符串占用多大内存? 你知道吗?

思考之后,下面我们就一起验证下,使用windbg去托管堆一查究竟,代码如下:

 static void Main(string[] args)
 {
  string s = string.Empty;

  Console.ReadLine();
 }

0:000> !clrstack -l
OS Thread Id: 0x308c (0)
 Child SP  IP Call Site
ConsoleApp6.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp6Program.cs @ 19]
 LOCALS:
 0x00000087391febd8 = 0x000002605da91420
0:000> !DumpObj /d 000002605da91420
Name: System.String
String: 
Fields:
  MT Field Offset   Type VT Attr  Value Name
00007ff9eb2b85a0 4000281 8  System.Int32 1 instance  0 m_stringLength
00007ff9eb2b6838 4000282 c  System.Char 1 instance  0 m_firstChar
00007ff9eb2b59c0 4000286 d8 System.String 0 shared  static Empty
     >> Domain:Value 000002605beb2230:NotInit <<
0:000> !objsize 000002605da91420
sizeof(000002605da91420) = 32 (0x20) bytes (System.String)

从图中你可以看到,仅仅一个空字符串就要占用 32byte,如果500w个空字符串就是: 32byte x 500w = 152M,是不是不算不知道,一算吓一跳。。。 这还仅仅是一个什么都没有的空字符串哦。

2. 回归到Trade

问题也已经摆出来了,接下来回归到Trade中,为了方便演示,先模拟以文件的形式从数据库读取20w的trade。

 class Program
 {
 static void Main(string[] args)
 {
  var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade()
  {
  TradeID = m,
  TradeFrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
     .ElementAt(m % 4)
  }).ToList();

  GC.Collect(); //方便测试,把临时变量清掉
  Console.WriteLine("执行成功");
  Console.ReadLine();
 }
 }

 class Trade
 {
 public int TradeID { get; set; }
 public string TradeFrom { get; set; }
 }

然后用windbg去跑一下托管堆,再量一下trades的大小。

0:000> !dumpheap -stat
Statistics:
  MT Count TotalSize Class Name
00007ff9eb2b59c0 200200 7010246 System.String

0:000> !objsize 0x000001a5860629a8
sizeof(000001a5860629a8) = 16097216 (0xf59fc0) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

从上面输出中可以看到托管堆有200200 = 20w(程序分配)+ 200(系统分配)个,然后再看size: