字符串拼接报错,字符十有八九是串连`String`对象在内存里撑爆了。别慌,接的结果解决这不是过长你代码写错了,是字符Java(或者C#、Python类似)对字符串的串连存储方式有硬限制。比如Java里字符串最大长度约`Integer.MAX_VALUE`个字符,接的结果解决大概21亿,过长听着挺大,字符可你要是串连循环拼接几百万次日志,或者把一个大文件逐行读进来拼成一个大字符串,接的结果解决照样能撞墙。过长
我遇到过最典型的字符场景:一个批量导出功能,把数据库里几万条记录循环拼成XML字符串。串连跑着跑着直接抛`OutOfMemoryError`,接的结果解决或者更阴险的——不报错,但程序卡死,GC疯狂回收又疯狂增长,最后整个服务假死。那会儿我盯着代码看了半天,才意识到问题出在`result += row`这种写法上。每次拼接都新建一个字符串对象,旧对象等着被回收,几万次循环下来,内存里堆了无数个中间状态的废弃字符串。
解决思路其实就一条:别用不可变字符串做累加器。换成可变容器,比如Java用`StringBuilder`或`StringBuffer`,Python用`list`再`join`,C#用`StringBuilder`。核心原理是这些容器内部维护一个可增长的字符数组,追加内容直接写进数组尾部,不会反复创建新对象。改完之后,内存占用从指数级增长变成线性增长,几万条记录轻松搞定。
但光换容器还不够。如果你拼接的是超大内容,比如几十MB的文本,`StringBuilder`默认扩容策略可能频繁拷贝数组。这时候可以预估大小,初始化时直接给足容量:`new StringBuilder(expectedSize)`。别小看这一步,能省掉大量数组复制的时间。我做过测试,同样拼10万条短字符串,不指定容量比指定容量慢将近一倍。
另一个容易被忽略的坑是流式处理。如果你根本不需要把完整字符串一次性放内存里,就别拼。比如写文件,直接`BufferedWriter`一行行写;传网络,用流式输出。这比任何字符串优化都彻底——压根不生成大字符串。我后来重构导出功能,改成边查数据库边写文件,内存占用从峰值几百MB降到几十MB,而且速度更快,因为省去了最后一次性序列化大对象的开销。
还有个偏门但实用的招:如果业务逻辑允许,把大字符串拆成多个小段处理。比如你要生成一个超长CSV,可以分段生成,每段几千行就写入文件或发送到下游,而不是全部攒在内存里。这跟流式思路一脉相承,但更灵活,适合需要中途校验或修改的场景。
最后说个反直觉的观察:很多人以为`StringBuilder`是万能药,其实在循环次数极少(比如几十次)时,直接用`+`拼接反而更清晰,性能差异可以忽略。别为了优化而优化,先测出瓶颈在哪,再动手改。字符串过长的问题,本质是内存管理问题,不是语法问题。你只要记住“可变容器优先、预估容量、能流式就别攒着”,基本能避开99%的坑。
顶: 98511踩: 735






评论专区