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

不知道何时开始,很多程序员喜欢用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被消灭掉了。

四:总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310180.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 2008 日_SQL2008 的 日期数据类型

摘要你是否曾经想在数据库中存储一个日期而没有时间部分&#xff0c;或者想存储一个时间值希望有更高的精度&#xff1f;在SQL Server 2008的介绍中&#xff0c;微软介绍了一些新的日期数据类允许你只存储一个日期、更高精度的时间值等。这些新的数据类型为你存储日期和时间相关…

别了,JavaScript;你好,Blazor

Web开发与JavaScript开发向来是同义词。直到WebAssembly的横空出世&#xff0c;WebAssembly (Wasm)是一种在浏览器中可以执行的二进制指令。WebAssembly 的 官方工具链 能够编译 C/C 代码&#xff0c;但许多社区也提供了不同语言的编译器&#xff0c;如 Rust&#xff0c;Python…

[Java基础]接口组成(默认方法,静态方法,私有方法)

默认方法: 代码如下: package MyInterfacePack;public interface MyInterface {void show1();void show2();// void show3();public default void show3(){System.out.println("show3");};}package MyInterfacePack;public class MyInterfaceImplOne implements …

linux jdk1.7 tomcat mysql_Linux环境搭建 jdk+tomcat+mysql

好久之前搭建的&#xff0c;现在记录下 防止下次配置去找安装jdk(jre也行),tomcat MySQL一、jdk安装及环境变量配置:我是用 jdk-8u191-linux-x64.tar.gz 安装的安装之前提前下载好 jdk版本 看你用什么版本吧 都一样的步骤 按照下面的步骤操作 要确保完全是一个新环境…

Eight HDU - 1043(八数码+搜索)

题意&#xff1a; 就是还原八数码。输出操作。 题目&#xff1a; The 15-puzzle has been around for over 100 years; even if you dont know it by that name, youve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all …

开源 一套 Blazor Server 端精致套件

Blazor 作为一种 Web 开发的新技术已经发展有一段时间了&#xff0c;有些人标称 无 JS 无 TS&#xff0c;我觉得有点误导新人的意味&#xff0c;也有人文章大肆宣传 Blazor 是 JavaScript 的终结者&#xff0c;是为了替代 JavaScript 而生的&#xff0c;我认为这些言论都太激进…

[Java基础]函数式接口

代码如下: package MyInterfacePack01;FunctionalInterface public interface MyInterface {void show(); }package MyInterfacePack01;public class MyInterfaceDemo {public static void main(String[] args){MyInterface my ()->System.out.println("hello world&q…

mysql单库用户隔离_Mysql数据库隔离级别

数据库事务四大特性(ACID)原子性(Atomicity)原子性是指事务中的操作要么全部成功&#xff0c;要么失败回滚。一致性(Consistency)一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态&#xff0c;也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转…

Tree Cutting POJ - 2378(树形DP)

题意&#xff1a;有n个谷仓有n-1条路连接&#xff0c;问最少删除哪几个点才能使得删除点后得到的连通图的加点数不大于n/2. 分析&#xff1a;求树的重心的变形题&#xff0c;poj3107的简单版&#xff0c;一遍dfs从叶子到根转移找出找到以每个节点为根的子树的结点数&#xff0…

从零搭建分布式文件系统MinIO比FastDFS要更合适

前两天跟大家分享了一篇关于如何利用FastDFS组件来自建分布式文件系统的文章&#xff0c;有兴趣的朋友可以阅读下《用asp.net core结合fastdfs打造分布式文件存储系统》。通过留言发现大家虽然感兴趣&#xff0c;但是都觉得部署比较麻烦。的确&#xff0c;fastdfs的部署很繁琐&…

ember.js mysql_用AWS部署ember.jspadrino应用系列之一

aws提供了一年免费试用服务。这里&#xff0c;记录下&#xff0c;配置aws和部署开源cms程序——维卡币操盘手的整个过程。主要内容包括&#xff1a;EC2实例的创建和设置——ruby环境和nginx配置&#xff0c;部署ruby应用。RDS实例的创建和使用——mysql数据库参数组的使用。S3库…

[Java基础]方法引用

代码如下: package PrintablePack;public interface Printable {void printString(String s);}package PrintablePack;public class PrintableDemo {public static void main(String[] args){usePrintable(s-> System.out.println(s));usePrintable(System.out::println);/…

Maximum Sum UVA - 108(连续子序列最大和—变形之子矩阵最大和)

题目大意&#xff1a;给出 n*n 的矩阵&#xff0c;找每隔数字之和最大的子矩阵&#xff0c;输出最大和。 解题思路&#xff1a;枚举矩阵左上和右下的坐标&#xff0c;分别合并子矩阵的每列&#xff0c;使得二维转化为一维&#xff0c;然后利用连续子序列最大和去做就行。 Tim…

甲方爸爸,大概你要的是代码生成器吧?

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区特邀嘉宾&#xff01;一1&#xff09;有一天&#xff0c;我的朋友Y童鞋分享了他正在做的一个内部开源项目&#xff0c;这个开源项目从外表上看&#xff0c;跟目前市场上那些代码生成器本没有特别…

使用pdf.js来预览pdf文件_适用于Dynamics365与PowerApps的注释预览组件

powerapps/dynamics365适用的注释预览/批量下载组件自定义组件为预览功能原生预览支持的文件类型:图像,zip,音频,pdf支持批量打包注释为zip下载到本地使用浏览器预览支持:音频,视频,图像,pdf,文本,xml,json等,理论上只需要浏览器支持打开的文件类型,均可预览使用方法:1.导入解决…

[PAT乙级]1001 害死人不偿命的(3n+1)猜想

卡拉兹(Callatz)猜想&#xff1a; 对任何一个正整数 n&#xff0c;如果它是偶数&#xff0c;那么把它砍掉一半&#xff1b;如果它是奇数&#xff0c;那么把 (3n1) 砍掉一半。这样一直反复砍下去&#xff0c;最后一定在某一步得到 n1。卡拉兹在 1950 年的世界数学家大会上公布了…

Sticks UVA - 307(切木棍 线性区间dp,线性dp,区间思想。)

题目大意&#xff1a;将n节木棒接成m个长度相等的木条&#xff0c;要求木条的长度尽可能的短 Time limit 3000 ms OS Linux George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return st…

消息队列,我只选RabbitMQ!

高并发架构是架构师的必修课&#xff0c;而消息队列&#xff0c;则是王冠上最闪亮的那颗明珠&#xff01;能否驾驭消息队列这款高并发神器&#xff0c;亦成为架构师的试金石。作为专注.NET领域十多年的老架构师&#xff0c;下面从队列本质、技术选型、实战应用三个方面&#xf…

python比较三个数_python经典练习题(三)

人生苦短&#xff0c;你需要python继续学习python第一题题目&#xff1a;输入三个整数 x,y,z&#xff0c;请把这三个数由小到大输出不借助sortnum1 int(input("请输入第一个数"))num2 int(input("请输入第二个数"))num3 int(input("请输入第三个数&qu…