字符串太占内存了,我想了各种奇思淫巧对它进行压缩 (2)

从上面的输出中可以看到,托管堆上string现在是:204 = 4(程序分配) + 200(系统分配)个,这4个就是字典中的4个哦,空间的话:6897216 /1024/1024= 6.57M,对应之前的 15.35M优化了将近60%。

虽然优化了60%,但这种优化是破坏性的优化,需要修改我的Trade结构,同时还要定义个Dictionary,而且还有不小幅度的修改业务逻辑,大家都知道线上的代码是能不改则不改,不改肯定没错,改出问题肯定是你兜着走,是吧,那问题就来了,如何最小化的修改而且还能压缩空间,有这样两全其美的事情吗???

2. 利用字符串驻留池

貌似一说出来,大家都如梦初醒,驻留池的出现就是为了解决这个问题,CLR会在内部维护了一个我刚才定义的字典机制,重复的字符串就不需要在堆上再次分配,直接存它的引用地址即可,如果你不清楚驻留池,建议看一下我这篇: https://www.cnblogs.com/huangxincheng/p/12799736.html

接下来只需要在tradefrom 字段包一层 string.Intern 即可,改动不要太小,代码如下:

static void Main(string[] args) { var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade() { TradeID = m, TradeFrom = string.Intern(File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt") .ElementAt(m % 4)), //包一层 string.Intern }).ToList(); GC.Collect(); //方便测试,把临时变量清掉 Console.WriteLine("执行成功"); Console.ReadLine(); }

然后用windbg抓一下托管堆。

0:000> !dumpheap -stat Statistics: MT Count TotalSize Class Name 00007ff9eb2b59c0 204 10386 System.String 0:000> !clrstack -l OS Thread Id: 0x13f0 (0) Child SP IP Call Site ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 27] LOCALS: 0x0000005e4d3ff0a8 = 0x000001f8a15129a8 0000005e4d3ff2b8 00007ff9ecd96c93 [GCFrame: 0000005e4d3ff2b8] 0:000> !objsize 0x000001f8a15129a8 sizeof(000001f8a15129a8) = 8497368 (0x81a8d8) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

观察后发现,当用了驻留池之后空间为: 8497368 /1024/1024 =8.1M,你可能有疑问,为什么和字典化相比内存要大24%呢? 仔细观察你会发现,当用驻留池后,List<Trade> 中的TradeFrom存的是string在堆中的内存地址,在x64机器上要占用8个字节,而字典化方式内存堆上Trade是不分配TradeFrom,而是用了一个byte来替代,总体来说相当于一个trade省了7byte的空间,然后用windbg看一下。

0:000> !da -length 1 -details 000001f8b16f9b68 Name: ConsoleApp6.Trade[] Size: 2097176(0x200018) bytes Array: Rank 1, Number of elements 262144, Type CLASS Fields: MT Field Offset Type VT Attr Value Name 00007ff9eb2b85a0 4000001 10 System.Int32 1 instance 0 <TradeID>k__BackingField 00007ff9eb2b59c0 4000002 8 System.String 0 instance 000001f8a1516030 <TradeFrom>k__BackingField 0:000> !DumpObj /d 000001f8a1516030 Name: System.String String: WAP

可以看到, 000001f8a1516030 就是 堆上 string=Wap的引用地址,这个地址占用了8byte空间。

再回头dump一下使用字典化方式的Trade,可以看到它是没有 <TradeFrom>k__BackingField 字段的。

0:000> !da -length 1 -details 000001ed52759ac0 Name: ConsoleApp6.Trade[] Size: 262168(0x40018) bytes Array: Rank 1, Number of elements 32768, Type CLASS Fields: MT Field Offset Type VT Attr Value Name 00007ff9eb2b85a0 4000002 8 System.Int32 1 instance 0 <TradeID>k__BackingField 00007ff9eb2b7d20 4000003 c System.Byte 1 instance 0 <TradeFromID>k__BackingField 三:总结

大家可以根据自己的情况使用,使用驻留池方式是改变最小的,简单粗暴,自己构建字典化虽然最省内存,但需要修正业务逻辑,这个风险自担哦。。。

如您有更多问题与我互动,扫描下方进来吧~

图片名称

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsxjp.html