16097216/1024/1024= 15.35M,这就是展示的所有原始情况。
二:压缩技巧分析
1. 使用字典化处理
其实在托管堆上有20w个字符串,但你仔细观察一下会发现其实就是4种状态的重复显示,要么一淘,要么淘宝。。。这就给了我优化机会,何不在获取数据的时候构建好OrderFrom的字典,然后在trade中附增一个TradeFromID记录字典中的映射值,因为特征值少,用byte就可以了,有了这个思想,可以把代码修改如下:
class Program
{
public static Dictionary<int, string> orderfromDict = new Dictionary<int, string>();
static void Main(string[] args)
{
var trades = Enumerable.Range(0, 20 * 10000).Select(m =>
{
var tradefrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
.ElementAt(m % 4);
var kv = orderfromDict.FirstOrDefault(k => k.Value == tradefrom);
if (kv.Key == 0)
{
orderfromDict.Add(orderfromDict.Count + 1, tradefrom);
}
var trade = new Trade() { TradeID = m, TradeFromID = (byte)kv.Key };
return trade;
}).ToList();
GC.Collect(); //方便测试,把临时变量清掉
Console.WriteLine("执行成功");
Console.ReadLine();
}
}
class Trade
{
public int TradeID { get; set; }
public byte TradeFromID { get; set; }
public string TradeFrom
{
get
{
return Program.orderfromDict[TradeFromID];
}
}
}
代码还是很简单的,接下来用windbg看一下空间到底压缩了多少?
0:000> !dumpheap -stat Statistics: MT Count TotalSize Class Name 00007ff9eb2b59c0 204 10386 System.String 0:000> !clrstack -l OS Thread Id: 0x2ce4 (0) Child SP IP Call Site ConsoleApp6.Program.Main(System.String[]) [C:dreamCsharpConsoleApp1ConsoleApp6Program.cs @ 42] LOCALS: 0x0000006f4d9ff078 = 0x0000016fdcf82ab8 0000006f4d9ff288 00007ff9ecd96c93 [GCFrame: 0000006f4d9ff288] 0:000> !objsize 0x0000016fdcf82ab8 sizeof(0000016fdcf82ab8) = 6897216 (0x693e40) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])
从上面的输出中可以看到,托管堆上string现在是:204 = 4(程序分配) + 200(系统分配)个,这4个就是字典中的4个哦,空间的话:6897216 /1024/1024= 6.57M,对应之前的 15.35M优化了将近60%。
虽然优化了60%,但这种优化是破坏性的优化,需要修改我的Trade结构,同时还要定义个Dictionary,而且还有不小幅度的修改业务逻辑,大家都知道线上的代码是能不改则不改,不改肯定没错,改出问题肯定是你兜着走,是吧,那问题就来了,如何最小化的修改而且还能压缩空间,有这样两全其美的事情吗???
2. 利用字符串驻留池
貌似一说出来,大家都如梦初醒,驻留池的出现就是为了解决这个问题,CLR会在内部维护了一个我刚才定义的字典机制,重复的字符串就不需要在堆上再次分配,直接存它的引用地址即可,如果你不清楚驻留池,建议看一下我这篇: https://www.jb51.net/article/189450.htm










