String的replace导致内存溢出
从一次内存溢出来看JDK的String应该怎么用
背景
JDK在String类中给我们提供的API,replace是个使用频率很高的的方法。因为他可以对字符串内容进行替换,只需要输入替换字符串和被替换字符串,就可以轻松得到你想要的字符串,功能非常强大。从JDK里的说明就能看出它有多方便了:
源码:
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
事故回放
好了,接下来描述的场景纯属虚构,如有雷同,那一定是你也踩过类似的坑!
今天产品MM找你实现一个功能,要求对你们网站上的一些不友好的评论使用“哔~”屏蔽掉,但是判断不友好的功能比较复杂,用到了NLP,所以用到了算法同事给你提供的接口判断,那么接下来事情就简单了:
我们只需要将NLP处理之后返回来的字符串作为String.replace(target, replacement)的入参target,"哔~"作为replacement就好了。相信熟练的你很快能噼里啪啦敲出以下代码:

// 调用NLP接口判断不友好的内容
List<String> waitForReplace = callRpcNLP(text);
if (waitForReplace == null || waitForReplace.size() == 0) {
return;
}
// 逐一进行替(和)换(谐)
for (String target : waitForReplace) {
if (target == null) {
continue;
}
text.replace(target, "哔~");
}
看起来很不错,各种校验也都有了,我的代码果然写得优美又健壮,你已经忍不住陶醉在自己的杰作中了,那么这样有没问题呢?
事实上,到了真正运行的时候,内存爆了!!!
案情分析
原因之一
那么到底发生了什么,debug之下,我们定位到replace方法,发现正是因为远程RPC接口返回了空字符串"",而空字符串作为replace的入参target时,会对任意字符串匹配多次匹配成功。我们用个简单点的字符串来做下实验:
String text = "hello";
System.out.println(text.replace("","*"));
打印:
哔!h哔!e哔!l哔!l哔!o哔!
大家能看到,替换后的字符串出现了6个“哔~”。也就是说,一个简简单单的"hello"字符串,在repalce运行之后,被""匹配出了6处需要替换的地方,那么如果不是一个"hello",而是一大段文本,一篇几千字的论文呢?到了这里,我们距离真相已经很近了,""会导致replace方法对字符串进行多次匹配。这是内存爆了的其中一个原因。
原因之二
至于另一个原因,就得说下replace的实现了,上面说到的字符串匹配,大家很容易想到正则表达式,实际上,replace内部也确实是通过调用正则表达式相关的API,来实现字符串匹配的。xxxxxxxxxx 原因之二至于另一个原因,就得说下replace的实现了,上面说到的字符串匹配,大家很容易想到正则表达式,实际上,replace内部也确实是通过调用正则表达式相关的API,来实现字符串匹配的。
而正则表达式实现字符串匹配,实际上是个很复杂的操作,replace(target, replacement)中的target,在正则表达式中称为"模式"(pattern),而匹配的过程,需要每次从字符串拿出一个字符和模式中的字符匹配。如果匹配成功,那么字符串拿出下一个字符,模式也拿出下一个字符,继续下一轮的匹配,但是我们知道,正则表达式支持使用点“.”匹配任意字符,星号"*"匹配任意数量的字符,所以整个匹配的过程需要递归进行,没法通过两个字符串简单地一对一移动指针来完成匹配。
总结
通过这次”事故“,我们知道了String.replace方法是有可能导致内存溢出的。总结下我们如何防止出现这类问题:
入参target不但需要做null校验,还要做""空字符串校验
防止直接输入大段文本进行匹配,可通过对文本分片实现
String的replace导致内存溢出的更多相关文章
- POI读写大数据量excel,解决超过几万行而导致内存溢出的问题
1. Excel2003与Excel2007 两个版本的最大行数和列数不同,2003版最大行数是65536行,最大列数是256列,2007版及以后的版本最大行数是1048576行,最大列数是16384 ...
- Restful规则及JPA导致内存溢出
HTTP动词 对于资源的具体操作类型,由HTTP动词表示. 常用的HTTP动词有下面五个(括号里是对应的SQL命令). GET(SELECT):从服务器取出资源(一项或多项). POST(CREATE ...
- WPF循环加载图片导致内存溢出的解决办法
程序场景:一系列的图片,从第一张到最后一张依次加载图片,形成“动画”. 生成BitmapImage的方法有多种: 1. var source=new BitmapImage(new Uri(" ...
- 图片--Android加载图片导致内存溢出(Out of Memory异常)
Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...
- 添加IFrame导致内存溢出的解决过程(IE浏览器,目前发现了原因,还未解决)
1. 现象 每次动态添加iframe时,iexplore.exe进程占据的内存都会增加(大概10M左右),不会自动释放,最终导致内存溢出 2. 解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...
- php查询mysql返回大量数据结果集导致内存溢出的解决方法
web开发中如果遇到php查询mysql返回大量数据导致内存溢出.或者内存不够用的情况那就需要看下MySQL C API的关联,那么究竟是什么导致php查询mysql返回大量数据时内存不够用情况? 答 ...
- Android加载图片导致内存溢出(Out of Memory异常)
Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...
- 使用NPOI或POI 导出Excel大数据(百万级以上),导致内存溢出的解决方案(NPOI,POI)
使用工具:POI(JAVA),NPOI(.Net) 致谢博主 Crazy_Jeff 提供的思路 一.问题描述: 导出任务数据量近100W甚至更多,导出的项目就会内存溢出,挂掉. 二.原因分析: 1.每 ...
- go-处理字符串导致内存溢出
今日用go来做字符的“+”连接操作,每次连接的字符串大致有10M左右,循环连接100次,直接导致go内存溢出了. // Text project main.go package main import ...
随机推荐
- Linux之scp命令的使用
Linux之scp命令的使用 1. scp简介 1.1 命令功能: scp是 secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令.linux的scp命令可 ...
- VS基本教程
首先打开vs,点击工具栏,文件----新建----项目 然后依次选择Visual C++----空项目-----命名---项目存放路径 接下来在解决方案资源管理器中选中源文件,右键----添加---- ...
- token和session的区别及其发展史
其实token与session的问题是一种时间与空间的博弈问题, session是空间换时间,而token是时间换空间. 一.发展史 很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为 ...
- LOJ3119. 「CTS2019 | CTSC2019」随机立方体 二项式反演
题目传送门 https://loj.ac/problem/3119 现在 BZOJ 的管理员已经不干活了吗,CTS(C)2019 和 NOI2019 的题目到现在还没与传上去. 果然还是 LOJ 好. ...
- BZOJ1822 [JSOI2010]Frozen Nova 冷冻波 二分+最大流
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=1822 题解 好久没做网络流的,都没有想到网络流... 首先暴力判断一下一个巫妖和一个精灵之间能 ...
- SPOJ7258 SUBLEX - Lexicographical Substring Search
传送门[洛谷] 心态崩了我有妹子 靠 我写的记忆化搜索 莫名WA了 然后心态崩了 当我正要改成bfs排序的时候 我灵光一动 md我写的i=0;i<25;i++??? 然后 改过来就A掉了T^T ...
- 人生苦短_我用Python_configparser/yaml对配置文件读取/写入操作_010
第一,我们先开始安装yaml库,configparser是自带库,yaml库是针对读取yml文件,configparser现阶段我只用于读取conf文件 首先:1)对象文件为:data.yml,下面的 ...
- pipelines和重定向命令
pipelines: command1 | command2 例如,ls -l /usr/bin | less,将输出结果作为 less 命令的输入结果,在standard output 中显示出来. ...
- mybatis框架之装饰模式
学习开源框架源码,除了储备点知识以便于与面试官互相忽略之外,我想最重要的还是去学习大神如何写代码,如何做到职责单一,如何做到可扩展等... 本篇,试着总结一下mybatis在缓存模块使用到的装饰模式. ...
- eval函数让我忧伤
今天首次接触这个eval函数,让我忧伤了一把.我把当成字符串拼接,结果错得天远地远.大体情况是下面这句代码,一个劲的给我报NameError: name 'qinfeng' is not define ...