日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/4 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串太占内存了,我想了各种奇思淫巧对它进行压缩 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一:背景

1. 講故事

在我們的一個全內存項目中,需要將一家大品牌店鋪小千萬的trade灌入到內存中,大家知道trade中一般會有訂單來源,省市區?,當把這些字段灌進去后,你會發現他們特別侵蝕內存,因為都是字符串類型,不知道大家對內存侵蝕性是不是很清楚,我就問一個問題。

Answer: 一個空字符串占用多大內存?你知道嗎?

思考之后,下面我們就一起驗證下,使用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:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.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.String0:000> !objsize 0x000001a5860629a8 sizeof(000001a5860629a8) = 16097216 (0xf59fc0) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

從上面輸出中可以看到托管堆有200200 = 20w(程序分配)+ 200(系統分配)個,然后再看size:?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.String0:000> !clrstack -l OS Thread Id: 0x2ce4 (0)Child SP IP Call Site ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 42]LOCALS:0x0000006f4d9ff078 = 0x0000016fdcf82ab80000006f4d9ff288 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.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.String0:000> !clrstack -l OS Thread Id: 0x13f0 (0)Child SP IP Call SiteConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 27]LOCALS:0x0000005e4d3ff0a8 = 0x000001f8a15129a80000005e4d3ff2b8 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 CLASSFields:MT Field Offset Type VT Attr Value Name00007ff9eb2b85a0 4000001 10 System.Int32 1 instance 0 <TradeID>k__BackingField00007ff9eb2b59c0 4000002 8 System.String 0 instance 000001f8a1516030 <TradeFrom>k__BackingField0: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 CLASSFields:MT Field Offset Type VT Attr Value Name00007ff9eb2b85a0 4000002 8 System.Int32 1 instance 0 <TradeID>k__BackingField00007ff9eb2b7d20 4000003 c System.Byte 1 instance 0 <TradeFromID>k__BackingField

三:總結

大家可以根據自己的情況使用,使用駐留池方式是改變最小的,簡單粗暴,自己構建字典化雖然最省內存,但需要修正業務邏輯,這個風險自擔哦。。。

總結

以上是生活随笔為你收集整理的字符串太占内存了,我想了各种奇思淫巧对它进行压缩的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 精品中文字幕在线观看 | 成人黄色免费观看 | 九色视频91 | 亚洲视频在线观看网址 | 国产精品久久久久久久久久久久久久 | 一区二区三区四区在线免费观看 | 性chinese天美传媒麻 | 四川黄色一级片 | 女儿的朋友5中汉字晋通话 欧美成人免费高清视频 | 狠狠综合久久av一区二区 | 青草视频免费看 | 岳奶大又白下面又肥又黑水多 | 亚洲精品一区二区口爆 | 精品无码一级毛片免费 | 17c一起操 | 国产精品久久久久久久久久直播 | 亚洲激情四射 | 激情图片在线观看 | 不卡av在线免费观看 | 红桃av在线 | 国产欧美另类 | 欧美视频一区二区三区 | 女性私密整形视频 | 女人的洗澡毛片毛多 | 国产精品久久久毛片 | 日本三级精品 | 日本精品免费在线观看 | 欧美区在线| 一级片黄色 | 三级91| 免费av免费看 | 亚洲人成亚洲人成在线观看 | 韩国视频一区 | 日韩精品一区二区av | 国产青草 | 无码精品国产一区二区三区免费 | 国产区小视频 | 91精品国产日韩91久久久久久 | 国产午夜成人久久无码一区二区 | 黄视频在线免费看 | 国产视频黄色 | 日韩一区二区不卡视频 | 一区二区在线免费观看视频 | 天天干免费视频 | 久久综合99| 欧洲精品视频在线 | 美女福利在线 | 国产精品2 | 综合网av | 久草免费在线视频观看 | 麻豆传媒在线播放 | 青青草手机在线观看 | 日韩性大片| 夜夜爽日日澡人人添 | 国产精品无码一区二区三 | 精品人妻伦一二三区久久 | 亚洲午夜精品福利 | 操批网站| 大尺度做爰床戏呻吟舒畅 | 最新福利在线 | 午夜激情啪啪 | 欧美成人精品欧美一 | 日本三级大全 | 欧美456 | 色爽 | a视频在线免费观看 | 国产制服av | 午夜国产福利在线观看 | 亚洲国产精品成人综合色在线婷婷 | 成人一区二区精品 | 99热这里只有精品9 日韩综合在线 | 日韩a级黄色片 | 最新啪啪网站 | 亚洲 日本 欧美 中文幕 | 欧洲成人av | 91尤物视频在线观看 | 日本中文字幕免费观看 | 超碰激情在线 | 黄色一级片在线播放 | 中文字幕 日韩有码 | 日本网站在线 | 久操青青 | 相亲对象是问题学生在线观看 | 黄色片中文字幕 | 超碰蜜桃| 欧美成人黄色 | 日韩欧美在线观看一区二区三区 | 97精品久久 | 国产精选网站 | 黄色片网站免费在线观看 | 成人做爰www免费看视频网站 | 日韩极品少妇 | 女人扒开腿让男人桶爽 | 亚洲精品短视频 | 久久精品久久久精品美女 | 日日免费视频 | 嫩草网站入口 | а√天堂8资源在线官网 | gogo亚洲国模私拍人体 |