不知道何时开始,很多程序员喜欢用ToLower,ToUpper去实现忽略大小写模式的字符串相等性比较,有可能这个习惯是从别的语言引进的,大胆猜测下是JS,为了不引起争论,我指的JS是技师的意思~

一:背景

1. 讲故事

在我们一个订单聚合系统中,每一笔订单都会标注来源,比如JD,Taobao,Etao,Shopex 等等一些渠道,UI上也提供高级配置输入自定义的订单来源,后来客户反馈输入xxx查询不出订单,这里就拿shopex为例,用户用小写的shopex查询,但系统中标注的是首字母大写的Shopex,所以自然无法匹配,为了解决这个问题开发小哥就统一转成大写做比对,用代码表示如下:

var orderfrom = "shopex".ToUpper();customerIDList = MemoryOrders.Where(i =>i.OrderFrom.ToUpper()==orderFrom).Select(i => i.CustomerId).ToList();

改完后就是这么牛的上线了,乍一看也没啥问题,结果一查询明显感觉比之前速度慢了好几秒,干脆多点几下,好咯。。。在监控中发现CPU和memory突高突低,异常波动,这位小哥又在写bug了,查了下代码问他为什么这么写,小哥说在js中就是这么比较的~~~

2. string.Compare 改造

其实在C#中面对忽略大小写形式的比较是有专门的方法,性能高而且还不费内存,它就是 string.Compare,所以把上面代码改成如下就可以了。

var orderfrom = "shopex";customerIDList = MemoryOrders.Where(string.Compare(i.TradeFrom, tradefrom,StringComparison.OrdinalIgnoreCase) == 0).Select(i => i.CustomerId).ToList();

这其中的 StringComparison.OrdinalIgnoreCase枚举就是用来忽略大小写的,上线之后除了CPU还是有点波动,其他都没有问题了。

二:为什么ToLower,ToUpper会有如此大的影响

为了方便演示,我找了一篇英文小短文,然后通过查询某一个单词来演示ToUpper为啥对cpu和memory以及查询性能都有如此大的影响,代码如下:

public static void Main(string[] args){var strList = "Hooray! It's snowing! It's time to make a snowman.James runs out. He makes a big pile of snow. He puts a big snowball on top. He adds a scarf and a hat. He adds an orange for the nose. He adds coal for the eyes and buttons.In the evening, James opens the door. What does he see? The snowman is moving! James invites him in. The snowman has never been inside a house. He says hello to the cat. He plays with paper towels.A moment later, the snowman takes James's hand and goes out.They go up, up, up into the air! They are flying! What a wonderful night!The next morning, James jumps out of bed. He runs to the door.He wants to thank the snowman. But he's gone.".Split(' ');var query = "snowman".ToUpper();for (int i = 0; i < strList.Length; i++){var str = strList[i].ToUpper();if (str == query)Console.WriteLine(str);}Console.ReadLine();}

1. 内存波动探究

既然内存有波动,说明内存里进了脏东西,学C#基础知识的时候应该知道string是不可变的,一旦有修改就会生成新的string,那就是说ToUpper之后会出现新的string,为了用数据佐证,用windbg演示一下。

0:000> !dumpheap -type System.String -stat
Statistics:MT    Count    TotalSize Class Name
00007ff8e7a9a120        1           24 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
00007ff8e7a99e98        1           80 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]]
00007ff8e7a9a378        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]][]
00007ff8e7a93200       19         2264 System.String[]
00007ff8e7a959c0      429        17894 System.String
Total 451 object

可以看到托管堆上有Count=429个string对象,那这个429怎么来的?组成:短文128个,ToUpper后128个,系统默认165个,query字符串2个,不明字符串6个,最后就是128 +128 + 165 + 2 + 6=429,然后随便抽几个看看。

!dumpheap -mt 00007ff8e7a959c0 > !DumpObj 000002244282a1f8


0:000> !DumpObj /d 0000017800008010
Name:        System.String
MethodTable: 00007ff8e7a959c0
EEClass:     00007ff8e7a72ec0
Size:        38(0x26) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      HOUSE.
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff8e7a985a0  4000281        8         System.Int32  1 instance                6 m_stringLength
00007ff8e7a96838  4000282        c          System.Char  1 instance               48 m_firstChar
00007ff8e7a959c0  4000286       d8        System.String  0   shared           static Empty>> Domain:Value  0000017878943bb0:NotInit  <<
0:000> !DumpObj /d 0000017800008248
Name:        System.String
MethodTable: 00007ff8e7a959c0
EEClass:     00007ff8e7a72ec0
Size:        40(0x28) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      SNOWMAN
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff8e7a985a0  4000281        8         System.Int32  1 instance                7 m_stringLength
00007ff8e7a96838  4000282        c          System.Char  1 instance               53 m_firstChar
00007ff8e7a959c0  4000286       d8        System.String  0   shared           static Empty>> Domain:Value  0000017878943bb0:NotInit  <<

查了两个全是大写的“HOUSE”,“SNOWMAN”,再回到我的场景有小百万订单,也就会在托管堆上生成小百万个string,如果再点一次又会生成小百万个,内存怎么会不突增呢。。。

2.cpu和查询时间探究

现在大家知道了堆上可能有几百万个string对象,这些对象的分配和释放给cpu造成了不小的压力,本身toUpper之后速度变慢,更惨的是还会造成gc颤抖式触发,一颤抖所有的thread都会被暂停开启回收,速度就更慢了。。。

三:string.Compare解析

再回过头来看一下string.Compare为什么这么????????,大家可以通过dnspy查看一下源码即可,里面有一个核心函数,如下图:

        // Token: 0x060004B8 RID: 1208 RVA: 0x00010C48 File Offset: 0x0000EE48[SecuritySafeCritical]private unsafe static int CompareOrdinalIgnoreCaseHelper(string strA, string strB){int num = Math.Min(strA.Length, strB.Length);fixed (char* ptr = &strA.m_firstChar){fixed (char* ptr2 = &strB.m_firstChar){char* ptr3 = ptr;char* ptr4 = ptr2;while (num != 0){int num2 = (int)(*ptr3);int num3 = (int)(*ptr4);if (num2 - 97 <= 25){num2 -= 32;}if (num3 - 97 <= 25){num3 -= 32;}if (num2 != num3){return num2 - num3;}ptr3++;ptr4++;num--;}return strA.Length - strB.Length;}}}

这段代码很精妙,巧妙的使用97,将两个字符串按照大写模式的ascii码进行逐一比较,相比在堆上搞一堆东西快捷的多。

然后我修改一下代码,看看此时堆上如何。。。

public static void Main(string[] args){...var query = "snowman";for (int i = 0; i < strList.Length; i++){if (string.Compare(strList[i], query, StringComparison.OrdinalIgnoreCase) == 0){Console.WriteLine(strList[i]);}}Console.ReadLine();}0:000> !dumpheap -type System.String -stat
Statistics:MT    Count    TotalSize Class Name
00007ff8e7a9a120        1           24 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
00007ff8e7a99e98        1           80 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]]
00007ff8e7a9a378        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Globalization.CultureData, mscorlib]][]
00007ff8e7a93200       19         2264 System.String[]
00007ff8e7a959c0      300        13460 System.String
Total 322 objects

从 System.String 中可以看到,现在的堆上是300个,而原来是429,相当于少了129个,也就是128个ToUpper加上1个Query中的ToUpper被消灭掉了。

四:总结

平时我们哪些不好的写法,在大量数据面前不堪一击,同时也是一次好的成长机会~

慎用ToLower和ToUpper,小心把你的系统给拖垮了相关推荐

  1. C语言函数:tolower()、toupper字符大小写转换函数。

    C语言函数:tolower().toupper字符大小写转换函数. int toupper( int c) int tolower( int c) tolower():将字母转为小写. toupper ...

  2. 用tolower()和toupper()来实现对string进行大小写转换

    int tolower(int c) 把给定的字母转换为小写字母. 函数原型: int tolower(int c); 参数 c:这是要被转换为小写的字母. 返回值:如果 c 有相对应的小写字母,则该 ...

  3. C 字符串转大小写strupr , strlowr, tolower, toupper

    转大写 char *strupr(char *str) {char *orign=str;for (; *str!='\0'; str++)*str = toupper(*str);return or ...

  4. C语言isupper,tolower,islower,toupper混合使用题

    程序运行结果:Ab2Cd3eF4 答案不是  aB2cD3Ef4  的原因是:tolower('A')=a;tolower('a')=a;但是未赋值给*p,所以无效果:而对于整数有效是因为(*p)++ ...

  5. C库函数(tolower/toupper)实现字母的大小写转换

    本文将介绍库函数实现字母的大小写转换,常用到的是在ctype.h(C++中是cctype)库文件下定义的函数方法.首先来看一下C下tolower/toupper函数实现原型: int tolower( ...

  6. Why “transform(s.begin(),s.end(),s.begin(),tolower)” can't be complied successfully?

    From: http://stackoverflow.com/questions/5539249/why-transforms-begin-s-end-s-begin-tolower-cant-be- ...

  7. atoi、atol、strtod、strtol、strtoul、gcvt、ecvt、fcvt、sprintf、toascii、tolower类型转换

    atof(将字符串转换成浮点型数) 相关函数 atoi,atol,strtod,strtol,strtoul 表头文件 #include 定义函数 double atof(const char npt ...

  8. linux常用c函数(中文版)

    都是linux的c函数东西略多,用页面搜索来查找吧. << Back to man.ChinaUnix.net isalnum(测试字符是否为英文或数字) 相关函数 isalpha,isd ...

  9. awk 4.0+ man手册翻译第一版本

    CentOS 7 上awk 4.0以上版本的man手册翻译第一版: 参考地址:http://www.cnblogs.com/wutao666/p/9732976.html 参考地址:https://w ...

最新文章

  1. Python简介、安装、更新、基本语法及数据类型
  2. 魅族Android10内测招募答案,10款机型升级Android 10!魅族Flyme即日起内测招募
  3. MySQL连接查询—笛卡尔乘积
  4. uni-app微信获取手机号,第一次解密总是失败
  5. evt参数是干啥用的_塑料凳子上的洞,是干啥用的?
  6. python可以计算复杂积分吗_python – Scipy:加快2D复数积分的计算
  7. oracle如何储存超长汉子_热水器该如何选择?!
  8. java .class 反编译工具推荐
  9. java转换投影坐标_GeoTools坐标转换(投影转换和仿射变换)
  10. iOS 蓝牙开发 swift (一)
  11. 什么是区块链? 区块链的入门教程~
  12. 程序设计c语言作业章春芳,C语言说课PPT
  13. 我们总能识别出团队中的坏苹果
  14. 【网络编程学习之旅】一文学习网络编程之NIO
  15. 思科面试经验| 2020-21年校园内虚拟招聘实习
  16. 给定字符串 s 和 t ,判断 s 是否为 t 的子序列
  17. 2020十大最佳大数据分析工具
  18. 案例5:Java大学生创新创业项目管理设计与实现任务书
  19. java的坐标转换_java版本坐标转换
  20. Metabase——开源的大数据分析探索、可视化报表神器

热门文章

  1. Delphi中的容器类(3)
  2. porting linux
  3. 小程序 iphone和安卓_如何阻止iPhone和iPad应用程序要求评级
  4. 如何从命令行浏览和连接到无线网络
  5. 深入MySQL存储引擎分析锁和排序的原理
  6. 天梯 L2 这是二叉搜索树吗?
  7. Cygwin使用指南
  8. 搭建 vue2 单元测试环境(karma+mocha+webpack3)
  9. @Springboot搭建项目controller层接收json格式的对象失败
  10. 甲骨文宣布供应链管理云平台支持LogFire仓库管理系统